I. Compiler Qt avec Vulkan▲
Lorsqu'on compile Qt depuis les fichiers source sur Windows, Linux ou Android, Vulkan est automatiquement activé dès que les en-têtes correspondants sont détectés par le compilateur. À noter que Windows est légèrement différent des autres systèmes d'exploitation, puisque Qt récupère automatiquement la variable VULKAN_SDK définie par le SDK Vulkan de LunarG.
Vérifiez la sortie de configure (ensuite disponible dans le fichier config.summary) :
Qt Gui:
...
Vulkan ................................. yes
...
Si la sortie affiche no, allez dans qtbase/config.tests/qpa/vulkan, lancez make et regardez pourquoi la compilation n'est pas possible.
Comme dit dans la partie 1, aucune des bibliothèques de Qt GUI ou des extensions n'a besoin d'être liée à la bibliothèque Vulkan ou à toute autre API. Il en va de même pour les applications Qt par défaut. Par conséquent, cela rend les choses très faciles : une version de Qt compilé avec Vulkan d'activé est parfaite pour le déploiement d'applications, tout comme pour les systèmes sans aucune bibliothèque Vulkan. Finis les problèmes avec les DLL manquantes ! Naturellement, si Vulkan n'est pas disponible à l'exécution, QVulkanInstance::create() ne fonctionnera pas et retournera false dans tous les cas. Notez aussi que les applications elles-mêmes peuvent choisir de se relier à la bibliothèque Vulkan si elles en ont besoin : il suffit donc d'ajouter LIBS += -lvulkan au fichier .pro.
II. Récupérer une instance Vulkan▲
Dans Vulkan, chaque état de l'application est stocké dans un objet VkInstance (référez-vous aux spécifications pour les détails). Dans Qt, les instances Vulkan sont représentées par des objets QVulkanInstance, une classe qui hérite de QPlatformVulkanInstance. Les extensions de plateforme QPA, au moins celles qui devraient gérer Vulkan, sont supposées fournir une implémentation de cette classe. Comme dit précédemment, ces plateformes sont actuellement Windows, xcb et Android.
En suivant le schéma habituel de QWindows et des classes QOpenGL, QVulkanInstance ne s'initialise qu'au moment où la fonction create() est appelée. La bibliothèque Vulkan (ou toute autre bibliothèque qui finit par charger Vulkan) se charge seulement à ce moment-là (notez quelques exceptions, voir plus bas).
L'instance VkInstance peut être retrouvée ensuite via vkInstance().
Sans surprise, la classe QVulkanInstance permet de spécifier les options de configuration de ladite instance, comme dans l'API désirée, et, plus important, la liste des couches et des extensions à activer.
Même si Qt autorise l'inclusion de couches et d'extensions non gérées, il peut arriver qu'il les filtre automatiquement. Il sera alors nécessaire d'examiner les noms et les versions de toutes les couches et les extensions gérées. Cela peut être fait à tout moment, même avant d'appeler la fonction create() grâce aux fonctions supportedExtensions() et supportedLayers(). Celles-ci provoqueront bien sûr un chargement de Vulkan qui précédera la fonction create().
Il est important de savoir que les extensions citées précédemment et qui sont requises pour les opérations de base telles que VK_KHR_surface ou VK_KHR_win32_surface sont automatiquement ajoutées à la liste d'inclusion par Qt.
III. Modèle pour main()▲
La fonction main() d'une application Qt utilisant Vulkan ressemblera à cela :
int
main(int
argc, char
**
argv)
{
QGuiApplication
app(argc, argv); // ou QApplication en cas d'utilisation de widgets
const
bool
enableLogsAndValidation =
...
QVulkanInstance
inst;
if
(enableLogsAndValidation) {
QLoggingCategory
::
setFilterRules(QStringLiteral
("qt.vulkan=true"
));
#ifndef Q_OS_ANDROID
inst.setLayers(QByteArrayList
() <<
"VK_LAYER_LUNARG_standard_validation"
);
#else
// voir Android-specifics sur https://developer.android.com/ndk/guides/graphics/validation-layer.html
inst.setLayers(QByteArrayList
()
<<
"VK_LAYER_GOOGLE_threading"
<<
"VK_LAYER_LUNARG_parameter_validation"
<<
"VK_LAYER_LUNARG_object_tracker"
<<
"VK_LAYER_LUNARG_core_validation"
<<
"VK_LAYER_LUNARG_image"
<<
"VK_LAYER_LUNARG_swapchain"
<<
"VK_LAYER_GOOGLE_unique_objects"
);
#endif
}
if
(!
inst.create())
qFatal
("Failed to create Vulkan instance: %d"
, inst.errorCode());
MyVulkanWindow w;
w.setVulkanInstance(&
inst);
w.resize(1024
, 768
);
w.show();
return
app.exec();
}
Dans la plupart des cas, il n'y aura qu'une seule instance QVulkan. Elle peut rester en mémoire, bien qu'elle doive être prête avant la création d'une QWindow ou d'objets de fenêtre dérivés de QVulkanWindow dès qu'ils devront être associés avec une QVulkanInstance. (Plus d'informations dans la partie 3.)
La catégorie de journalisation qt.vulkan peut être très pratique en cas de problèmes. Lorsqu'elle est activée, les instances de QVulkanInstance et, le cas échéant, de QVulkanWindow afficheront toutes les deux leurs problèmes dans le flux de sortie et en particulier durant leur initialisation. La fonction setFilerRules() appelée dans l'extrait de code ci-dessus fonctionne très bien pour les plateformes où les variables d'environnement (QT_LOGGING_RULES) ont des problèmes, bien que ce ne soit pas forcément la meilleure approche. Sur Windows et Linux, il vaut mieux contrôler cela avec des fichiers de configuration.
En ce qui concerne le flux de sortie de Vulkan et des couches de validation, QVulkanInstance redirige automatiquement ces messages dans qDebug. Cette redirection est activée par défaut grâce à VK_EXT_debug_report. Si vous ne la désirez pas, changez le drapeau correspondant avant d'appeler create().
IV. Utiliser des moteurs graphiques externes▲
Durant le développement de Qt 5, on attribua une importance croissante à Qt Quick et aux moteurs OpenGL externes. Cela a conduit à la sortie de QtQuickRenderControl, l'amélioration de QOpenGLContext pour récupérer un objet OpenGL natif déjà existant et à bien d'autres évolutions.
Dans le même esprit, QVulkanInstance permet de réutiliser une instance Vulkan existante. Pour y arriver, il suffit d'appeler setVkInstance() avant create(). De cette façon, chaque aspect de la création d'une instance Vulkan incombe à l'application ou à toute autre API, QVulkanInstance encadrant simplement l'instance Vulkan fournie à la place d'en créer une nouvelle.
C'est fini pour aujourd'hui, à plus pour la partie 3 !
V. Remerciements▲
Au nom de toute l'équipe Qt, j'aimerais adresser le plus grand remerciement à KDAB pour nous avoir autorisés à traduire cet article !
Je tiens à remercier Thibaut Cuvelier et Alexandre Laurent pour leurs conseils et relectures, ainsi que Claude Leloup pour ses corrections.