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