Améliorations de l’utilisation de mémoire et du processeur dans Qt 3D

La performance et la stabilité de Qt 3D ne cessent de s’améliorer, grâce au travail acharné de moult contributeurs. Sa performance est principalement due à son architecture très flexible, qui sépare la partie visible (le rendu) des calculs (les aspects, responsables de la gestion des entrées et des animations, notamment). Ces aspects fonctionnent en parallèle, sur d’autres fils d’exécution, afin d’exploiter au mieux les processeurs multicœurs modernes ; ils nécessitent également une certaine duplication des données, pour éviter les synchronisations inutiles. De plus, les aspects de Qt 3D exécutent leurs travaux en définissant un certain nombre de tâches et leurs dépendances, afin d’avoir une granularité maximale dans l’ordonnancement des tâches. Pour le moment, Qt 3D est beaucoup retravaillé, au vu des plans futurs pour Qt 3D Studio.

L’utilisation de processeur de Qt 3D a énormément diminué depuis sa première version, en Qt 5.6, jusque la prochaine à venir, Qt 5.11. Ces améliorations sont dues à bon nombre de facteurs : des améliorations du solveur de dépendances entre tâches (qui s’exécute plus rapidement et donne de meilleures solutions), de QThreadPool (une classe de Qt Core utilisée pour gérer une série de fils d’exécution, mais en quantité moindre que requise pour Qt 3D), des caches de certaines tâches très gourmandes en CPU (comme QMaterialParameterGathererJob, dont le code était entièrement réexécuté à chaque image).

Avec le même genre de travail, la consommation de mémoire a franchement diminué, surtout dans les applications les plus simples. Par exemple, la démo avec une bille qui tourne consomme quarante-cinq mégaoctets en moins (nonante-deux avec Qt 5.6.3, quarante-sept avec Qt 5.9.4). Une application plus complexe, du style de celles réalisées avec Qt 3D Studio, passe de deux cent vingt à cent trente-cinq (infographie).

Sources et images : CPU usage improvements in Qt 3D, Memory usage improvements in Qt3D.

Advertisements

Quelles sont les améliorations à venir dans l’extension Qt pour Visual Studio ?

L’intégration de Qt dans Visual Studio n’a pas toujours été très utilisable, il a fallu un certain temps avant que la deuxième version soit rendue publique et prenne en charge les versions les plus récentes de Visual Studio. La prochaine version sera numérotée 2.2 et apportera un grand nombre de changements pour en faciliter l’utilisation.

L’un des plus gros changements sera l’utilisation de MSBuild pour gérer la compilation et l’intégration des générateurs de code de Qt, à savoir moc, rcc et uic. Jusqu’à présent, ces outils sont appelés dans une étape supplémentaire de compilation, indiquée pour chaque fichier séparément. Cette approche était la seule possible pour les versions antérieures de Visual Studio, mais a maintenant de sérieux problèmes, surtout pour les plus gros projets. En particulier, on ne peut pas définir une telle étape pour tous les fichiers d’un certain type — avec comme inconvénients une performance réduite et des fichiers de projet extrêmement gros. Aussi, ces étapes ne peuvent pas avoir leur propre page de configuration, contrairement aux autres parties de la compilation comme l’édition des lien ou la compilation proprement dite.

MSBuild, au contraire, se base principalement sur les fichiers de projet (.vcxproj) pour définir les opérations de compilation. Des règles définissent tous les outils prédéfinis, que ce soit le compilateur ou l’éditeur de liens : chacun peut être appliqué sur un certain sous-ensemble de fichiers. Des outils externes peuvent aussi être ajoutés, en utilisant une syntaxe similaire à ceux qui sont prédéfinis, avec une flexibilité totale — et une page de paramètres pour l’utilisateur.

La prochaine version des Qt Visual Studio Tools inclura des règles MSBuild et des cibles spécifiques à moc, rcc et uic. Les nouveaux projets utiliseront automatiquement ces définitions (même lors de l’importation d’un fichier de projet Qt), les anciens devront être convertis (mais cette opération ne sera pas obligatoire). Cependant, les fichiers de projet Visual Studio resteront portables : ils ne requerront pas l’installation de l’extension.

Cette modification en profondeur de l’extension a des avantages non négligeables en termes de performance. Ainsi, pour la version précédente, ajouter dix fichiers à un projet avec vingt configurations prenait presque quarante-cinq minutes ! Presque l’intégralité de ce temps était pris par la modification des propriétés pour les fichiers générés. Avec la version 2.2, la même opération ne prendra plus que… trois secondes ! En moyenne, les temps sont réduits de nonante-cinq pour cent.

Voir aussi : Qt in Visual Studio: A New Approach Based on MSBuild, Qt in Visual Studio: Improving Performance.

Sortie de Qt Creator 4.6 Beta

Qt Creator 4.6 s’annonce comme une version avec un certain nombre d’améliorations d’utilisabilité, mais sans révolution majeure ; la première préversion Beta est maintenant de sortie. Le modèle de code C++ utilisant Clang a été fortement retravaillé. Notamment, il utilise maintenant Clang 5.0 (au lieu de l’antique 3.9), ce qui lui ouvre la compatibilité avec C++17 ; les informations de type sur les symboles proviennent également de Clang, de telle sorte que les variables auto sont correctement résolues. Également, clang-tidy et Clazy peuvent être intégrés aux diagnostics affichés dans l’éditeur de code. Toutes ces fonctionnalités doivent cependant être activées manuellement.

Pour faciliter la navigation dans le code, la barre d’outil correspondante dispose de trois filtres supplémentaires. “b ” (avec un espace, donc) permet de sauter directement à un favori dans le code. “t ” lance un item du menu principal (Fichiers pour la plupart des plateformes) : ainsi, “t sess expe” correspond à File > Sessions > Experimental Stuff. “= ” évalue une expression JavaScript, avec le même moteur d’exécution que Qt Quick ; la seule exception est que les fonctions de l’objet Math sont directement disponibles : ainsi, on peut directement taper max(1, 2) au lieu de Math.max(1, 2).

L’éditeur de modèles a été fortement retravaillé pour cette version. Il gère désormais l’alignement du texte et les noms d’objet sur plusieurs lignes. L’export vers une image peut se faire sur certains éléments ou sur tout le diagramme. Plus de panneaux comprennent le glisser-déposer.

Voir aussi : la liste exhaustive des changements.

Source : Qt Creator 4.6 Beta released.

Sortie de Clazy 1.3

Clazy est un outil d’analyse statique prévu spécifiquement pour Qt. Ses versions précédentes ont déjà permis d’inciter les développeurs à suivre moult meilleures pratiques, mais ne se sont pas encore attaquées aux signaux, slots et connexions : Clazy 1.3 vient combler ce manque.

Un premier cas géré est celui où quelque chose qui n’est pas un signal est pourtant connecté comme s’il en était un. Cependant, sans Clazy, un code comme celui-ci compilerait sans problème, puisqu’il n’y a pas d’erreur de syntaxe C++ — juste une erreur de sémantique Qt.

// warning: MyObj::mySlot is not a signal [-Wclazy-connect-non-signal]
connect(obj, &MyObj::mySlot, &MyObj::mySlot2);

Une variable locale ne peut pas être capturée dans une fonction anonyme si elle est utilisée dans une connexion : en effet, quand le signal correspondant sera appelé, le contexte courant sera probablement détruit… et le code plantera lamentablement.

void myFunction()
{
    int localVar = ...;
 
    // program will crash when signal is emitted!
    connect(m_object, &MyObj::mySignal, [&localVar] { ... }); 
}

La forme de connect() avec trois arguments ne devrait pas être utilisée, puisque celle avec un argument de contexte permet d’effectuer certaines vérifications à l’exécution et donc d’éviter certains plantages (dans l’exemple suivant, si m_receiver est supprimé).

connect(m_widget, &MyWidget::textChanged, [this] (const QString &text) {
    m_receiver->update(text, false);
});
connect(m_widget, &MyWidget::textChanged, m_receiver,
        [this] (const QString &text) { (....) });

Qt::UniqueConnection ne peut pas être garanti quand le receveur du signal est un foncteur, une fonction anonyme ou une fonction globale, mais aucun avertissement ne peut être généré par le compilateur, d’où l’apport de Clazy.

Ajouter des slots dans une classe qui hérite de QThread n’est pas forcément un problème en soi, mais est souvent un signe de code mal conçu : en effet, ces slots sont exécutés dans le fil d’exécution correspondant à l’objet QThread, mais pas dans le fil QThread. Il vaut souvent mieux utiliser des objets travailleurs.

Toute utilisation de l’ancienne syntaxe avec SIGNAL() et SLOT() est aussi signalée : en effet, elle ne permet pas au compilateur d’effectuer beaucoup de vérifications, contrairement à celle utilisant des pointeurs vers des fonctions membre.

Si certains types ne sont pas normalisés avec les macros SIGNAL(), SLOT(), Q_ARG() ou Q_RETURN_ARG(), quelques allocations de mémoire inutiles sont effectuées.

// warning: Signature is not normalized. Use void mySlot(int) instead of void mySlot(const int) [-Wclazy-connect-not-normalized]
o.connect(&o, SIGNAL(mySignal(int, int)),
          &o, SLOT(void mySlot(const int)));

Les API surchargeant les signaux d’une classe de base sont souvent difficiles à utiliser et causent des défauts difficiles à trouver. De plus, syntaxiquement, Qt permet de surcharger un signal avec une méthode qui n’en est pas un et vice-versa. Clazy signale tous ces cas, ainsi que ceux où un signal est marqué comme virtuel (moc affichait déjà un avertissement à l’exécution dans ces cas).

Souvent, un slot n’est pas une méthode constante (comme pourrait l’être un accesseur). Un signal n’a pas besoin d’être marqué comme constant. Ces cas arrivent cependant fréquemment lors d’une interaction avec du code QML, mais il vaut mieux utiliser Q_PROPERTY ou Q_INVOKABLE qu’un slot.

Pour des raisons de lisibilité, les signaux devraient toujours être émis avec le mot clé emit (ou la macro Q_EMIT), non comme de simples méthodes. De même, ces mot clé et macro ne devraient être utilisés qu’avec des signaux.

Les connexions par nom sont une vieille fonctionnalité, qui n’a jamais été très populaire et ne devrait plus être utilisée. De fait, simplement renommer un objet pourrait casser des connexions — ces constructions sont donc extrêmement fragiles. La nouvelle syntaxe de connexion rend les choses explicites à la compilation.

Les propriétés Q_PROPERTY non constantes devraient toujours avoir une méthode NOTIFY, pour prévenir de tout changement de valeur. Bien que les utilisations de ces méthodes ne se soient réellement popularisées que depuis QML, n’importe qui pourrait en avoir besoin — GammaRay, par exemple.

Source : Nailing 13 signal and slot mistakes with clazy 1.3.

Augmenter le nombre de lumières de Qt 3D

Bien que Qt 3D gère sans problème des scènes avec un très grand nombre de sources lumineuses grâce à des techniques de rendu différé, les matériaux par défaut du module Qt 3D Extras sont, quant à eux, limités à seulement huit lumières : de fait, ils sont très liés à un mode de rendu direct. Quelques trucs et astuces permettent de définir plus de sources de lumière dans le code, mais le rendu, lui, n’en utilisera jamais plus de huit. Par exemple, dans cette vidéo, le code définit cent lumières, mais seulement huit parmi elles sont utilisées pour le rendu, les huit plus proches en fonction de l’état de la scène.

Cette limitation est assez gênante pour Qt 3D, même s’il est possible d’utiliser d’autres techniques de rendu pour gérer plus de lumière (c’est notamment le cas du Qt 3D Studio Runtime). Elle est due à OpenGL ; plus précisément, au nombre d’implémentations d’OpenGL, sur du matériel extrêmement divers, des cartes graphiques modernes très haut de gamme à des puces bon marché utilisées dans certaines applications embarquées : parfois, les limitations en termes de quantité de données transférables sont énormes… et Qt 3D doit fonctionner raisonnablement bien sur tout ce matériel.

Les derniers développements à ce sujet utilisent le “graphe de shader“, apparu il y a peu dans Qt 3D. La technique implémentée est assez basique : sur des cartes qui en sont capables, toutes les lumières sont utilisées ; sur du matériel nettement moins puissant, le nombre d’instructions des shaders est fortement réduit (et le nombre de lumières gérées en même temps).

Ce graphe de shader est une véritable révolution pour Qt 3D. Il permet de générer des shaders à la volée, en utilisant une structure similaire au graphe des matériaux de la plupart des applications de modélisation 3D actuelles. Chaque nœud de ce graphe de shader correspond à une fonction : il s’agit d’un calcul à effectuer entre une série d’entrées et une série de sorties. Par exemple, le nœud d’une surface prend en entrée les angles d’incidence et de réflexion d’un rayon de lumière sur la surface, la couleur de ce rayon et de la surface, pour calculer la contribution à la luminosité. En connectant suffisamment de ces nœuds dans un graphe, on définit le flot de calculs effectués par un shader, que Qt 3D transforme alors dans un langage de programmation qui dépend du matériel et de l’API utilisés.

Pour le cas particulier des sources de lumière, il devient très facile de créer un nœud pour chaque source de la scène. Ensuite, le code généré peut être aussi efficace que possible pour gérer toutes ces lumières (avec un shader par source de lumière plutôt qu’un seul programme qui itère sur toutes les sources).

Source : Increasing the number of lights in Qt 3D.

Sortie de Cutelyst 1.12

Cutelyst, le framework Web basé sur Qt, s’apprête à passer à la version 2. En attendant cette nouvelle étape dans la vie du projet, la version 1.12 est sortie. Principalement, elle apporte une protection contre les attaques CSRF (cross-site request forgery) : par exemple, un site factice propose un formulaire comme le site d’origine, mais en récupérant des informations au passage ; avec cette protection CSRF, le véritable site n’acceptera pas la soumission du formulaire (il manquera une marque CSRF, générée de manière unique pour chaque affichage du formulaire réel).

Quelques améliorations de performance ont aussi été intégrées au projet, notamment au niveau de la boucle d’événements : elle peut gérer nettement plus de connexions simultanées, en étant deux à trois fois plus rapide que l’implémentation précédente (celle par défaut de glib).

Cutelyst 2.0 nous attend au tournant. Cette version devrait être fortement compatible avec l’actuelle (même s’il faut s’attendre à des changements mineurs d’API et d’ABI, hormis la suppression de toute syntaxe désapprouvée), tout en apportant une fonctionnalité maintenant essentielle : l’implémentation du protocole HTTP/2.

Source : Cutelyst 1.12.0 released.

Sortie de Qbs 1.10

Le remplaçant de qmake pour la compilation, Qbs, vient de sortir en version 1.10, qui marque une nouvelle étape importante vers une version pleinement utilisable dans tous les contextes. Tous les trois mois, des fonctionnalités importantes sont ajoutées : avec Qbs 1.9, il s’agissait de la paramétrisation des dépendances et du multiplexage des produits (notamment pour Android, afin de compiler l’application pour plusieurs plateformes) ; avec Qbs 1.8, une simplification de quelques syntaxes.

La version 1.10 cherche encore à améliorer les questions de syntaxe. Notamment, la définition de profils peut se faire de manière dynamique, grâce au composant Profile ; ceci devrait être utile pour les projets qui ont des prérequis sur l’environnement de compilation ou de déploiement spécifiques et bien connus. L’imbrication de groupes est plus naturelle au niveau des préfixes : si un groupe ne définit pas sa propriété de préfixe, sa valeur sera prise du groupe parent, de manière récursive au besoin. Qbs peut automatiquement appeler le compilateur Qt Quick et qmlcachegen au besoin.

Les étiqueteurs de fichiers et les modules peuvent définir des niveaux de priorité, ce qui facilite le travail de Qbs pour la désambiguïsation des implémentations acceptables d’un même module (ces priorités permettent le développement d’un résolveur de conflits). En pratique, les composants Module et FileTagger disposent maintenant d’une propriété de priorité : en cas de choix, à l’exécution, le composant avec la plus haute priorité sera choisi. En pratique, par exemple, certains modules pour Android sont disponibles en deux versions : Linux et Android. Cependant, la version Android est un surensemble de l’implémentation Linux générique, elle a donc une priorité supérieure, ce qui lui garantit d’être choisie.

La documentation de Qbs a aussi été retravaillée. Principalement, avec la version 1.10, deux nouvelles pages ont été écrites : un guide de portage depuis qmake, avec une vue d’ensemble des variables de qmake et la manière d’atteindre le même objectif avec Qbs ; un guide sur la création de modules pour des bibliothèques externes. Les options en ligne de commande sont aussi mieux décrites. Qbs 1.11 continuera cet important travail de documentation (Qbs devrait remplacer définitivement qmake d’ici à Qt 6), avec notamment bien plus de guides pratiques.

Télécharger Qbs 1.10.

Source : Qbs 1.10 released.