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 | \qhashold{QVulkanLayer} |
305 | */ |
306 | |
307 | /*! |
308 | \class QVulkanExtension |
309 | \inmodule QtGui |
310 | \brief Represents information about a Vulkan extension. |
311 | */ |
312 | |
313 | /*! |
314 | \variable QVulkanExtension::name |
315 | \brief The name of the extension. |
316 | */ |
317 | |
318 | /*! |
319 | \variable QVulkanExtension::version |
320 | \brief The version of the extension. This is an integer, increasing with each backward |
321 | compatible change. |
322 | */ |
323 | |
324 | /*! |
325 | \fn bool operator==(const QVulkanExtension &lhs, const QVulkanExtension &rhs) |
326 | \since 5.10 |
327 | \relates QVulkanExtension |
328 | |
329 | Returns \c true if Vulkan extensions \a lhs and \a rhs are have the |
330 | same name and version. |
331 | */ |
332 | |
333 | /*! |
334 | \fn bool operator!=(const QVulkanExtension &lhs, const QVulkanExtension &rhs) |
335 | \since 5.10 |
336 | \relates QVulkanExtension |
337 | |
338 | Returns \c true if Vulkan extensions \a lhs and \a rhs are have different |
339 | name or version. |
340 | */ |
341 | |
342 | /*! |
343 | \fn size_t qHash(const QVulkanExtension &key, size_t seed = 0) |
344 | \since 5.10 |
345 | \qhashold{QVulkanExtension} |
346 | */ |
347 | |
348 | /*! |
349 | \class QVulkanInfoVector |
350 | \inmodule QtGui |
351 | \brief A specialized QList for QVulkanLayer and QVulkanExtension. |
352 | */ |
353 | |
354 | /*! |
355 | \fn template<typename T> bool QVulkanInfoVector<T>::contains(const QByteArray &name) const |
356 | |
357 | \return true if the list contains a layer or extension with the given \a name. |
358 | */ |
359 | |
360 | /*! |
361 | \fn template<typename T> bool QVulkanInfoVector<T>::contains(const QByteArray &name, int minVersion) const |
362 | |
363 | \return true if the list contains a layer or extension with the given |
364 | \a name and a version same as or newer than \a minVersion. |
365 | */ |
366 | |
367 | /*! |
368 | \fn QVulkanInfoVector<QVulkanLayer> QVulkanInstance::supportedLayers() const |
369 | \return the list of supported instance-level layers. |
370 | |
371 | \note This function can be called before create(). |
372 | */ |
373 | |
374 | /*! |
375 | \internal |
376 | */ |
377 | QVulkanInfoVector<QVulkanLayer> QVulkanInstance::supportedLayers() |
378 | { |
379 | return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedLayers() : QVulkanInfoVector<QVulkanLayer>(); |
380 | } |
381 | |
382 | /*! |
383 | \fn QVulkanInfoVector<QVulkanExtension> QVulkanInstance::supportedExtensions() const |
384 | \return the list of supported instance-level extensions. |
385 | |
386 | \note This function can be called before create(). |
387 | */ |
388 | |
389 | /*! |
390 | \internal |
391 | */ |
392 | QVulkanInfoVector<QVulkanExtension> QVulkanInstance::supportedExtensions() |
393 | { |
394 | return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedExtensions() : QVulkanInfoVector<QVulkanExtension>(); |
395 | } |
396 | |
397 | /*! |
398 | \return the version of instance-level functionality supported by the Vulkan |
399 | implementation. |
400 | |
401 | In practice this is either the value returned from |
402 | vkEnumerateInstanceVersion, if that function is available (with Vulkan 1.1 |
403 | and newer), or 1.0. |
404 | |
405 | Applications that want to branch in their Vulkan feature and API usage |
406 | based on what Vulkan version is available at run time, can use this function |
407 | to determine what version to pass in to setApiVersion() before calling |
408 | create(). |
409 | |
410 | \note This function can be called before create(). |
411 | |
412 | \sa setApiVersion() |
413 | */ |
414 | QVersionNumber QVulkanInstance::supportedApiVersion() const |
415 | { |
416 | return d_ptr->ensureVulkan() ? d_ptr->platformInst->supportedApiVersion() : QVersionNumber(); |
417 | } |
418 | |
419 | /*! |
420 | Makes QVulkanInstance adopt an existing VkInstance handle instead of |
421 | creating a new one. |
422 | |
423 | \note \a existingVkInstance must have at least \c{VK_KHR_surface} and the |
424 | appropriate WSI-specific \c{VK_KHR_*_surface} extensions enabled. To ensure |
425 | debug output redirection is functional, \c{VK_EXT_debug_utils} is needed as |
426 | well. |
427 | |
428 | \note This function can only be called before create() and has no effect if |
429 | called afterwards. |
430 | */ |
431 | void QVulkanInstance::setVkInstance(VkInstance existingVkInstance) |
432 | { |
433 | if (isValid()) { |
434 | qWarning(msg: "QVulkanInstance already created; setVkInstance() has no effect" ); |
435 | return; |
436 | } |
437 | |
438 | d_ptr->vkInst = existingVkInstance; |
439 | } |
440 | |
441 | /*! |
442 | Configures the behavior of create() based on the provided \a flags. |
443 | |
444 | \note This function can only be called before create() and has no effect if |
445 | called afterwards. |
446 | */ |
447 | void QVulkanInstance::setFlags(Flags flags) |
448 | { |
449 | if (isValid()) { |
450 | qWarning(msg: "QVulkanInstance already created; setFlags() has no effect" ); |
451 | return; |
452 | } |
453 | |
454 | d_ptr->flags = flags; |
455 | } |
456 | |
457 | /*! |
458 | Specifies the list of instance \a layers to enable. It is safe to specify |
459 | unsupported layers as well because these get ignored when not supported at |
460 | run time. |
461 | |
462 | \note This function can only be called before create() and has no effect if |
463 | called afterwards. |
464 | */ |
465 | void QVulkanInstance::setLayers(const QByteArrayList &layers) |
466 | { |
467 | if (isValid()) { |
468 | qWarning(msg: "QVulkanInstance already created; setLayers() has no effect" ); |
469 | return; |
470 | } |
471 | |
472 | d_ptr->layers = layers; |
473 | } |
474 | |
475 | /*! |
476 | Specifies the list of additional instance \a extensions to enable. It is |
477 | safe to specify unsupported extensions as well because these get ignored |
478 | when not supported at run time. |
479 | |
480 | \note The surface-related extensions required by Qt (for example, \c |
481 | VK_KHR_win32_surface) will always be added automatically, no need to |
482 | include them in this list. |
483 | |
484 | \note \c VK_KHR_portability_enumeration is added automatically unless the |
485 | NoPortabilityDrivers flag is set. This value was introduced in Qt 6.5. |
486 | |
487 | \note This function can only be called before create() and has no effect if |
488 | called afterwards. |
489 | */ |
490 | void QVulkanInstance::setExtensions(const QByteArrayList &extensions) |
491 | { |
492 | if (isValid()) { |
493 | qWarning(msg: "QVulkanInstance already created; setExtensions() has no effect" ); |
494 | return; |
495 | } |
496 | |
497 | d_ptr->extensions = extensions; |
498 | } |
499 | |
500 | /*! |
501 | Specifies the highest Vulkan API version the application is designed to use. |
502 | |
503 | By default \a vulkanVersion is 0, which maps to Vulkan 1.0. |
504 | |
505 | \note This function can only be called before create() and has no effect if |
506 | called afterwards. |
507 | |
508 | \note Be aware that Vulkan 1.1 changes the behavior with regards to the |
509 | Vulkan API version field. In Vulkan 1.0 specifying an unsupported \a |
510 | vulkanVersion led to failing create() with \c VK_ERROR_INCOMPATIBLE_DRIVER, |
511 | as was mandated by the specification. Starting with Vulkan 1.1, the |
512 | specification disallows this, the driver must accept any version without |
513 | failing the instance creation. |
514 | |
515 | Application developers are advised to familiarize themselves with the \c |
516 | apiVersion notes in |
517 | \l{https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html}{the |
518 | Vulkan specification}. |
519 | |
520 | \sa supportedApiVersion() |
521 | */ |
522 | void QVulkanInstance::setApiVersion(const QVersionNumber &vulkanVersion) |
523 | { |
524 | if (isValid()) { |
525 | qWarning(msg: "QVulkanInstance already created; setApiVersion() has no effect" ); |
526 | return; |
527 | } |
528 | |
529 | d_ptr->apiVersion = vulkanVersion; |
530 | } |
531 | |
532 | /*! |
533 | Initializes the Vulkan library and creates a new or adopts and existing |
534 | Vulkan instance. |
535 | |
536 | \return true if successful, false on error or when Vulkan is not supported. |
537 | |
538 | When successful, the pointer to this QVulkanInstance is retrievable via |
539 | \l {QVulkanInstance::}{vkInstance()}. |
540 | |
541 | The Vulkan instance and library is available as long as this |
542 | QVulkanInstance exists, or until destroy() is called. |
543 | |
544 | By default the VkInstance is created with the flag |
545 | \l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkInstanceCreateFlagBits.html}{VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR} |
546 | set. This means that Vulkan Portability physical devices get enumerated as |
547 | well. If this is not desired, set the NoPortabilityDrivers flag. |
548 | */ |
549 | bool QVulkanInstance::create() |
550 | { |
551 | if (isValid()) |
552 | destroy(); |
553 | |
554 | if (!d_ptr->ensureVulkan()) |
555 | return false; |
556 | |
557 | d_ptr->platformInst->createOrAdoptInstance(); |
558 | |
559 | if (d_ptr->platformInst->isValid()) { |
560 | d_ptr->vkInst = d_ptr->platformInst->vkInstance(); |
561 | d_ptr->layers = d_ptr->platformInst->enabledLayers(); |
562 | d_ptr->extensions = d_ptr->platformInst->enabledExtensions(); |
563 | d_ptr->errorCode = VK_SUCCESS; |
564 | d_ptr->funcs.reset(new QVulkanFunctions(this)); |
565 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
566 | d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); |
567 | return true; |
568 | } |
569 | |
570 | qWarning(msg: "Failed to create platform Vulkan instance" ); |
571 | if (d_ptr->platformInst) { |
572 | d_ptr->errorCode = d_ptr->platformInst->errorCode(); |
573 | d_ptr->platformInst.reset(); |
574 | } else { |
575 | d_ptr->errorCode = VK_NOT_READY; |
576 | } |
577 | return false; |
578 | } |
579 | |
580 | /*! |
581 | Destroys the underlying platform instance, thus destroying the VkInstance |
582 | (when owned). The QVulkanInstance object is still reusable by calling |
583 | create() again. |
584 | */ |
585 | void QVulkanInstance::destroy() |
586 | { |
587 | d_ptr->reset(); |
588 | } |
589 | |
590 | /*! |
591 | \return true if create() was successful and the instance is valid. |
592 | */ |
593 | bool QVulkanInstance::isValid() const |
594 | { |
595 | return d_ptr->platformInst && d_ptr->platformInst->isValid(); |
596 | } |
597 | |
598 | /*! |
599 | \return the Vulkan error code after an unsuccessful create(), \c VK_SUCCESS otherwise. |
600 | |
601 | The value is typically the return value from vkCreateInstance() (when |
602 | creating a new Vulkan instance instead of adopting an existing one), but |
603 | may also be \c VK_NOT_READY if the platform plugin does not support Vulkan. |
604 | */ |
605 | VkResult QVulkanInstance::errorCode() const |
606 | { |
607 | return d_ptr->errorCode; |
608 | } |
609 | |
610 | /*! |
611 | \return the VkInstance handle this QVulkanInstance wraps, or \nullptr if |
612 | create() has not yet been successfully called and no existing instance has |
613 | been provided via setVkInstance(). |
614 | */ |
615 | VkInstance QVulkanInstance::vkInstance() const |
616 | { |
617 | return d_ptr->vkInst; |
618 | } |
619 | |
620 | /*! |
621 | \return the requested flags. |
622 | */ |
623 | QVulkanInstance::Flags QVulkanInstance::flags() const |
624 | { |
625 | return d_ptr->flags; |
626 | } |
627 | |
628 | /*! |
629 | \return the enabled instance layers, if create() was called and was successful. The |
630 | requested layers otherwise. |
631 | */ |
632 | QByteArrayList QVulkanInstance::layers() const |
633 | { |
634 | return d_ptr->layers; |
635 | } |
636 | |
637 | /*! |
638 | \return the enabled instance extensions, if create() was called and was |
639 | successful. The requested extensions otherwise. |
640 | */ |
641 | QByteArrayList QVulkanInstance::extensions() const |
642 | { |
643 | return d_ptr->extensions; |
644 | } |
645 | |
646 | /*! |
647 | \return the requested Vulkan API version against which the application |
648 | expects to run, or a null version number if setApiVersion() was not called |
649 | before create(). |
650 | */ |
651 | QVersionNumber QVulkanInstance::apiVersion() const |
652 | { |
653 | return d_ptr->apiVersion; |
654 | } |
655 | |
656 | /*! |
657 | Resolves the Vulkan function with the given \a name. |
658 | |
659 | For core Vulkan commands prefer using the function wrappers retrievable from |
660 | functions() and deviceFunctions() instead. |
661 | */ |
662 | PFN_vkVoidFunction QVulkanInstance::getInstanceProcAddr(const char *name) |
663 | { |
664 | // The return value is PFN_vkVoidFunction instead of QFunctionPointer or |
665 | // similar because on some platforms honoring VKAPI_PTR is important. |
666 | return d_ptr->platformInst->getInstanceProcAddr(name); |
667 | } |
668 | |
669 | /*! |
670 | \return the platform Vulkan instance corresponding to this QVulkanInstance. |
671 | |
672 | \internal |
673 | */ |
674 | QPlatformVulkanInstance *QVulkanInstance::handle() const |
675 | { |
676 | return d_ptr->platformInst.data(); |
677 | } |
678 | |
679 | /*! |
680 | \return the corresponding QVulkanFunctions object that exposes the core |
681 | Vulkan command set, excluding device level functions, and is guaranteed to |
682 | be functional cross-platform. |
683 | |
684 | \note The returned object is owned and managed by the QVulkanInstance. Do |
685 | not destroy or alter it. |
686 | |
687 | The functions from the core Vulkan 1.0 API will be available always. When it |
688 | comes to higher Vulkan versions, such as, 1.1 and 1.2, the QVulkanFunctions |
689 | object will try to resolve the core API functions for those as well, but if |
690 | the Vulkan instance implementation at run time has no support for those, |
691 | calling any such unsupported function will lead to unspecified behavior. In |
692 | addition, to properly enable support for Vulkan versions higher than 1.0, an |
693 | appropriate instance API version may need to be set by calling |
694 | setApiVersion() before create(). To query the Vulkan implementation's |
695 | instance-level version, call supportedApiVersion(). |
696 | |
697 | \sa deviceFunctions(), supportedApiVersion() |
698 | */ |
699 | QVulkanFunctions *QVulkanInstance::functions() const |
700 | { |
701 | return d_ptr->funcs.data(); |
702 | } |
703 | |
704 | /*! |
705 | \return the QVulkanDeviceFunctions object that exposes the device level |
706 | core Vulkan command set and is guaranteed to be functional cross-platform. |
707 | |
708 | \note The Vulkan functions in the returned object must only be called with |
709 | \a device or a child object (VkQueue, VkCommandBuffer) of \a device as |
710 | their first parameter. This is because these functions are resolved via |
711 | \l{https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetDeviceProcAddr.html}{vkGetDeviceProcAddr} |
712 | in order to avoid the potential overhead of internal dispatching. |
713 | |
714 | \note The returned object is owned and managed by the QVulkanInstance. Do |
715 | not destroy or alter it. |
716 | |
717 | \note The object is cached so calling this function with the same \a device |
718 | again is a cheap operation. However, when the device gets destroyed, it is up |
719 | to the application to notify the QVulkanInstance by calling |
720 | resetDeviceFunctions(). |
721 | |
722 | The functions from the core Vulkan 1.0 API will be available always. When |
723 | it comes to higher Vulkan versions, such as, 1.1 and 1.2, the |
724 | QVulkanDeviceFunctions object will try to resolve the core API functions |
725 | for those as well, but if the Vulkan physical device at run time has no |
726 | support for those, calling any such unsupported function will lead to |
727 | unspecified behavior. To properly enable support for Vulkan versions higher |
728 | than 1.0, an appropriate instance API version may need to be set by calling |
729 | setApiVersion() before create(). In addition, applications are expected to |
730 | check the physical device's apiVersion in VkPhysicalDeviceProperties. |
731 | |
732 | \sa functions(), resetDeviceFunctions() |
733 | */ |
734 | QVulkanDeviceFunctions *QVulkanInstance::deviceFunctions(VkDevice device) |
735 | { |
736 | QVulkanDeviceFunctions *&f = d_ptr->deviceFuncs[device]; |
737 | if (!f) |
738 | f = new QVulkanDeviceFunctions(this, device); |
739 | return f; |
740 | } |
741 | |
742 | /*! |
743 | Invalidates and destroys the QVulkanDeviceFunctions object for the given |
744 | \a device. |
745 | |
746 | This function must be called when a VkDevice, for which deviceFunctions() |
747 | was called, gets destroyed while the application intends to continue |
748 | running, possibly creating a new logical Vulkan device later on. |
749 | |
750 | There is no need to call this before destroying the QVulkanInstance since |
751 | clean up is then performed automatically. |
752 | |
753 | \sa deviceFunctions() |
754 | */ |
755 | void QVulkanInstance::resetDeviceFunctions(VkDevice device) |
756 | { |
757 | QVulkanDeviceFunctions *&f = d_ptr->deviceFuncs[device]; |
758 | delete f; |
759 | f = nullptr; |
760 | } |
761 | |
762 | /*! |
763 | Creates or retrieves the already existing \c{VkSurfaceKHR} handle for the |
764 | given \a window. |
765 | |
766 | \return the Vulkan surface handle or 0 when failed. |
767 | */ |
768 | VkSurfaceKHR QVulkanInstance::surfaceForWindow(QWindow *window) |
769 | { |
770 | QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); |
771 | // VkSurfaceKHR is non-dispatchable and maps to a pointer on x64 and a uint64 on x86. |
772 | // Therefore a pointer is returned from the platform plugin, not the value itself. |
773 | void *p = nativeInterface->nativeResourceForWindow(QByteArrayLiteral("vkSurface" ), window); |
774 | return p ? *static_cast<VkSurfaceKHR *>(p) : VK_NULL_HANDLE; |
775 | } |
776 | |
777 | /*! |
778 | \return true if the queue family with \a queueFamilyIndex within the |
779 | \a physicalDevice supports presenting to \a window. |
780 | |
781 | Call this function when examining the queues of a given Vulkan device, in |
782 | order to decide which queue can be used for performing presentation. |
783 | */ |
784 | bool QVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) |
785 | { |
786 | return d_ptr->platformInst->supportsPresent(physicalDevice, queueFamilyIndex, window); |
787 | } |
788 | |
789 | /*! |
790 | This function should be called by the application's renderer before queuing |
791 | a present operation for \a window. |
792 | |
793 | While on some platforms this will be a no-op, some may perform windowing |
794 | system dependent synchronization. For example, on Wayland this will |
795 | add send a wl_surface.frame request in order to prevent the driver from |
796 | blocking for minimized windows. |
797 | |
798 | \since 5.15 |
799 | */ |
800 | void QVulkanInstance::presentAboutToBeQueued(QWindow *window) |
801 | { |
802 | d_ptr->platformInst->presentAboutToBeQueued(window); |
803 | } |
804 | |
805 | /*! |
806 | This function should be called by the application's renderer after queuing |
807 | a present operation for \a window. |
808 | |
809 | While on some platforms this will be a no-op, some may perform windowing |
810 | system dependent synchronization. For example, on X11 this will update |
811 | \c{_NET_WM_SYNC_REQUEST_COUNTER}. |
812 | */ |
813 | void QVulkanInstance::presentQueued(QWindow *window) |
814 | { |
815 | d_ptr->platformInst->presentQueued(window); |
816 | } |
817 | |
818 | /*! |
819 | \typedef QVulkanInstance::DebugFilter |
820 | |
821 | Typedef for debug filtering callback functions, with the following signature: |
822 | |
823 | \code |
824 | bool myDebugFilter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, |
825 | size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage) |
826 | \endcode |
827 | |
828 | Returning \c true suppresses the printing of the message. |
829 | |
830 | \note Starting with Qt 6.5 \c{VK_EXT_debug_utils} is used instead of the |
831 | deprecated \c{VK_EXT_debug_report}. The callback signature is based on |
832 | VK_EXT_debug_report. Therefore, not all arguments can be expected to be |
833 | valid anymore. Avoid relying on arguments other than \c pMessage, \c |
834 | messageCode, and \c object. Applications wishing to access all the callback |
835 | data as specified in VK_EXT_debug_utils should migrate to DebugUtilsFilter. |
836 | |
837 | \sa installDebugOutputFilter(), removeDebugOutputFilter() |
838 | */ |
839 | |
840 | /*! |
841 | \overload |
842 | |
843 | Installs a \a filter function that is called for every Vulkan debug |
844 | message. When the callback returns \c true, the message is stopped (filtered |
845 | out) and will not appear on the debug output. |
846 | |
847 | \note Filtering is only effective when NoDebugOutputRedirect is not |
848 | \l{setFlags()}{set}. Installing filters has no effect otherwise. |
849 | |
850 | \note This function can be called before create(). |
851 | |
852 | \sa removeDebugOutputFilter() |
853 | */ |
854 | void QVulkanInstance::installDebugOutputFilter(DebugFilter filter) |
855 | { |
856 | if (!d_ptr->debugFilters.contains(t: filter)) { |
857 | d_ptr->debugFilters.append(t: filter); |
858 | if (d_ptr->platformInst) |
859 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
860 | } |
861 | } |
862 | |
863 | /*! |
864 | \overload |
865 | |
866 | Removes a \a filter function previously installed by |
867 | installDebugOutputFilter(). |
868 | |
869 | \note This function can be called before create(). |
870 | |
871 | \sa installDebugOutputFilter() |
872 | */ |
873 | void QVulkanInstance::removeDebugOutputFilter(DebugFilter filter) |
874 | { |
875 | d_ptr->debugFilters.removeOne(t: filter); |
876 | if (d_ptr->platformInst) |
877 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
878 | } |
879 | |
880 | /*! |
881 | \typedef QVulkanInstance::DebugUtilsFilter |
882 | |
883 | Typedef for debug filtering callback functions, with the following signature: |
884 | |
885 | \code |
886 | std::function<bool(DebugMessageSeverityFlags severity, DebugMessageTypeFlags type, const void *message)>; |
887 | \endcode |
888 | |
889 | The \c message argument is a pointer to the |
890 | VkDebugUtilsMessengerCallbackDataEXT structure. Refer to the documentation |
891 | of \c{VK_EXT_debug_utils} for details. The Qt headers do not use the real |
892 | type in order to avoid introducing a dependency on post-1.0 Vulkan headers. |
893 | |
894 | Returning \c true suppresses the printing of the message. |
895 | |
896 | \sa installDebugOutputFilter(), removeDebugOutputFilter() |
897 | \since 6.5 |
898 | */ |
899 | |
900 | /*! |
901 | \enum QVulkanInstance::DebugMessageSeverityFlag |
902 | \since 6.5 |
903 | |
904 | \value VerboseSeverity |
905 | \value InfoSeverity |
906 | \value WarningSeverity |
907 | \value ErrorSeverity |
908 | */ |
909 | |
910 | /*! |
911 | \enum QVulkanInstance::DebugMessageTypeFlag |
912 | \since 6.5 |
913 | |
914 | \value GeneralMessage |
915 | \value ValidationMessage |
916 | \value PerformanceMessage |
917 | */ |
918 | |
919 | /*! |
920 | Installs a \a filter function that is called for every Vulkan debug |
921 | message. When the callback returns \c true, the message is stopped (filtered |
922 | out) and will not appear on the debug output. |
923 | |
924 | \note Filtering is only effective when NoDebugOutputRedirect is not |
925 | \l{setFlags()}{set}. Installing filters has no effect otherwise. |
926 | |
927 | \note This function can be called before create(). |
928 | |
929 | \sa clearDebugOutputFilters() |
930 | \since 6.5 |
931 | */ |
932 | void QVulkanInstance::installDebugOutputFilter(DebugUtilsFilter filter) |
933 | { |
934 | d_ptr->debugUtilsFilters.append(t: filter); |
935 | if (d_ptr->platformInst) |
936 | d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); |
937 | } |
938 | |
939 | /*! |
940 | Removes all filter functions installed previously by |
941 | installDebugOutputFilter(). |
942 | |
943 | \note This function can be called before create(). |
944 | |
945 | \sa installDebugOutputFilter() |
946 | \since 6.5 |
947 | */ |
948 | void QVulkanInstance::clearDebugOutputFilters() |
949 | { |
950 | d_ptr->debugFilters.clear(); |
951 | d_ptr->debugUtilsFilters.clear(); |
952 | if (d_ptr->platformInst) { |
953 | d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); |
954 | d_ptr->platformInst->setDebugUtilsFilters(d_ptr->debugUtilsFilters); |
955 | } |
956 | } |
957 | |
958 | #ifndef QT_NO_DEBUG_STREAM |
959 | QDebug operator<<(QDebug dbg, const QVulkanLayer &layer) |
960 | { |
961 | QDebugStateSaver saver(dbg); |
962 | dbg.nospace() << "QVulkanLayer(" << layer.name << " " << layer.version |
963 | << " " << layer.specVersion << " " << layer.description << ")" ; |
964 | return dbg; |
965 | } |
966 | |
967 | QDebug operator<<(QDebug dbg, const QVulkanExtension &extension) |
968 | { |
969 | QDebugStateSaver saver(dbg); |
970 | dbg.nospace() << "QVulkanExtension(" << extension.name << " " << extension.version << ")" ; |
971 | return dbg; |
972 | } |
973 | #endif |
974 | |
975 | QT_END_NAMESPACE |
976 | |