Qt Quick Arborescence de scène
Le graphe de scène dans Qt Quick
Qt Quick 2 utilise un graphe de scène dédié qui est ensuite parcouru et rendu via une API graphique telle qu’OpenGL ES, OpenGL, Vulkan, Metal ou Direct 3D. L'utilisation d'un graphe de scène pour le rendu graphique, plutôt que des systèmes de dessin impératifs traditionnels (QPainter, etc.), permet de conserver la scène à afficher d'une image à l'autre et de connaître l'ensemble complet des primitives à afficher avant le début du rendu. Cela ouvre la voie à un certain nombre d'optimisations, telles que le rendu par lots pour minimiser les changements d'état et l'élimination des primitives masquées.
Par exemple, supposons qu’une interface utilisateur contienne une liste de dix éléments, chacun comportant une couleur d’arrière-plan, une icône et un texte. Avec les techniques de dessin traditionnelles, cela entraînerait 30 appels de dessin et un nombre similaire de changements d’état. Un graphe de scène, en revanche, pourrait réorganiser les primitives à afficher de manière à ce que tous les arrière-plans soient dessinés en un seul appel, puis toutes les icônes, puis tout le texte, réduisant ainsi le nombre total d’appels de dessin à seulement 3. Ce type de regroupement et de réduction des changements d’état peut considérablement améliorer les performances sur certains matériels.
Le graphe de scène est étroitement lié à l’ Qt Quick e 2.0 et ne peut pas être utilisé de manière autonome. Le graphe de scène est géré et rendu par la classe ` QQuickWindow `, et les types d’éléments personnalisés peuvent ajouter leurs primitives graphiques dans le graphe de scène via un appel à la fonction ` QQuickItem::updatePaintNode()`.
Le graphe de scène est une représentation graphique de la scène d’éléments, une structure indépendante contenant suffisamment d’informations pour afficher tous les éléments. Une fois configuré, il peut être manipulé et affiché indépendamment de l’état des éléments. Sur de nombreuses plateformes, le graphe de scène sera même rendu sur un thread de rendu dédié pendant que le thread de l’interface graphique prépare l’état de l’image suivante.
Remarque : la plupart des informations figurant sur cette page concernent spécifiquement le comportement intégré par défaut du graphe de scène « Qt Quick ». Lorsque vous utilisez une autre adaptation du graphe de scène, telle que l’adaptation « software », tous les concepts ne s’appliquent pas nécessairement. Pour plus d’informations sur les différentes adaptations du graphe de scène, consultez la section Adaptations du graphe de scène.
Qt Quick Structure du graphe de scène
Le graphe de scène est composé d’un certain nombre de types de nœuds prédéfinis, chacun ayant une fonction spécifique. Bien que nous l’appelions « graphe de scène », une définition plus précise serait « arbre de nœuds ». L’arbre est construit à partir des types d’ QQuickItem s de la scène QML et, en interne, la scène est ensuite traitée par un moteur de rendu qui l’affiche. Les nœuds eux-mêmes ne contiennent ni code de dessin actif, ni fonction d'paint() virtuelle.
Même si l’arborescence de nœuds est principalement constituée en interne par les types QML d’ Qt Quick s existants, les utilisateurs ont également la possibilité d’ajouter des sous-arborescences complètes avec leur propre contenu, y compris des sous-arborescences représentant des modèles 3D.
Nœuds
Le nœud le plus important pour les utilisateurs est l’ QSGGeometryNode. Il sert à définir des graphismes personnalisés en spécifiant sa géométrie et son matériau. La géométrie est définie à l’aide d’ QSGGeometry et décrit la forme ou le maillage de la primitive graphique. Il peut s’agir d’une ligne, d’un rectangle, d’un polygone, de plusieurs rectangles disjoints ou d’un maillage 3D complexe. Le matériau définit la manière dont les pixels de cette forme sont remplis.
Un nœud peut avoir un nombre illimité d’enfants, et les nœuds de géométrie seront rendus de manière à apparaître dans l’ordre hiérarchique, les parents se trouvant derrière leurs enfants.
Remarque : cela ne donne aucune indication sur l’ordre de rendu réel dans le moteur de rendu. Seul le résultat visuel est garanti.
Les nœuds disponibles sont les suivants :
Implémente la fonctionnalité de découpage dans l’arborescence de scène | |
Utilisé pour tout le contenu rendu dans le graphe de scène | |
La classe de base pour tous les nœuds du graphe de scène | |
Utilisé pour modifier l'opacité des nœuds | |
Implémente les transformations dans le graphe de scène |
Les nœuds personnalisés sont ajoutés au graphe de scène en créant une sous-classe de ` QQuickItem::updatePaintNode()` et en activant l'indicateur ` QQuickItem::ItemHasContents `.
Avertissement : il est essentiel que les opérations graphiques natives (OpenGL, Vulkan, Metal, etc.) et les interactions avec le graphe de scène se déroulent exclusivement sur le thread de rendu, principalement lors de l’appel à la fonction updatePaintNode(). En règle générale, il convient de n’utiliser que les classes préfixées par « QSG » au sein de la fonction QQuickItem::updatePaintNode().
Pour plus de détails, consultez la section « Graphique de scène - Géométrie personnalisée ».
Prétraitement
Les nœuds disposent d’une fonction virtuelle ` QSGNode::preprocess()`, qui sera appelée avant le rendu du graphe de scène. Les sous-classes de nœuds peuvent activer l’indicateur ` QSGNode::UsePreprocess ` et redéfinir la fonction ` QSGNode::preprocess()` afin d’effectuer les derniers préparatifs de leur nœud. Par exemple, diviser une courbe de Bézier en un niveau de détail adapté au facteur d’échelle actuel ou mettre à jour une partie d’une texture.
Propriété des nœuds
La propriété des nœuds est soit attribuée explicitement par le créateur, soit gérée par le graphe de scène via l’activation de l’indicateur ` QSGNode::OwnedByParent`. Il est souvent préférable d’attribuer la propriété au graphe de scène, car cela simplifie le nettoyage lorsque le graphe de scène réside en dehors du thread de l’interface graphique.
Matériaux
Le matériau décrit comment l'intérieur d'une géométrie dans un « QSGGeometryNode » est rempli. Il encapsule les shaders graphiques pour les étapes de sommet et de fragment du pipeline graphique et offre une grande flexibilité quant aux résultats obtenus, bien que la plupart des éléments d’ Qt Quick s n’utilisent eux-mêmes que des matériaux très basiques, tels que des remplissages de couleur unie ou de texture.
Pour les utilisateurs qui souhaitent simplement appliquer un ombrage personnalisé à un type d’élément QML, il est possible de le faire directement en QML à l’aide du type « ShaderEffect ».
Vous trouverez ci-dessous la liste complète des classes de matériaux :
Moyen pratique de rendre des formes géométriques de couleur unie dans le graphe de scène | |
Encapsule l'état de rendu d'un programme de shader | |
Représente un programme de shader indépendant de l’API graphique | |
Utilisé comme jeton de type unique en combinaison avec QSGMaterial | |
Moyen pratique de rendre des géométries texturées dans l’arborescence de scène | |
Moyen pratique de rendre une géométrie texturée dans le graphe de scène | |
Moyen pratique de rendre une géométrie colorée par sommet dans l'arborescence de scène |
Nœuds pratiques
L’API du graphe de scène est de bas niveau et privilégie les performances plutôt que la facilité d’utilisation. Écrire des géométries et des matériaux personnalisés à partir de zéro, même les plus basiques, nécessite une quantité non négligeable de code. C’est pourquoi l’API inclut quelques classes de commodité afin de rendre les nœuds personnalisés les plus courants facilement accessibles.
- QSGSimpleRectNode - une sous-classe de ` QSGGeometryNode ` qui définit une géométrie rectangulaire avec un matériau de couleur unie.
- QSGSimpleTextureNode - une sous-classe de ` QSGGeometryNode ` qui définit une géométrie rectangulaire avec un matériau texturé.
Arborescence de scène et rendu
Le rendu de l’arbre de scène s’effectue en interne dans la classe ` QQuickWindow `, et il n’existe pas d’API publique permettant d’y accéder. Il existe toutefois quelques points dans le pipeline de rendu où l'utilisateur peut intégrer du code d'application. Cela permet d'ajouter du contenu personnalisé au graphe de scène ou d'insérer des commandes de rendu arbitraires en appelant directement l'API graphique (OpenGL, Vulkan, Metal, etc.) utilisée par le graphe de scène. Les points d'intégration sont définis par la boucle de rendu.
Pour une description détaillée du fonctionnement du moteur de rendu du graphe de scène, consultez la section « Moteur de rendu par défaut du graphe de scène » (Qt Quick Scene Graph Default Renderer).
Deux variantes de boucle de rendu sont disponibles : basic et threaded. basic est mono-thread, tandis que threaded effectue le rendu du graphe de scène sur un thread dédié. Qt tente de choisir une boucle adaptée en fonction de la plateforme et, éventuellement, des pilotes graphiques utilisés. Lorsque ce choix n’est pas satisfaisant, ou à des fins de test, la variable d’environnement ` QSG_RENDER_LOOP ` peut être utilisée pour forcer l’utilisation d’une boucle donnée. Pour vérifier quelle boucle de rendu est utilisée, activez l’option ` qt.scenegraph.general ` logging category.
Boucle de rendu multithread (« threaded »)
Sur de nombreuses configurations, le rendu de l’arborescence de scène s’effectue sur un thread de rendu dédié. Cela permet d’accroître le parallélisme des processeurs multicœurs et de mieux exploiter les temps d’attente, tels que ceux liés à un appel bloquant de swap de tampon. Cela offre des gains de performances significatifs, mais impose certaines restrictions quant au moment et à l’endroit où l’interaction avec l’arborescence de scène peut avoir lieu.
Voici un aperçu simple de la manière dont une image est rendue à l'aide de la boucle de rendu multithread et d'OpenGL. Les étapes sont les mêmes avec d'autres API graphiques, à l'exception des spécificités du contexte OpenGL.

- Une modification survient dans la scène QML, ce qui déclenche l'appel de la méthode `
QQuickItem::update()`. Cela peut résulter, par exemple, d'une animation ou d'une entrée utilisateur. Un événement est envoyé au thread de rendu pour lancer une nouvelle image. - Le thread de rendu se prépare à dessiner une nouvelle image et déclenche un blocage sur le thread de l’interface graphique.
- Pendant que le thread de rendu prépare la nouvelle image, le thread de l’interface graphique appelle ` QQuickItem::updatePolish()` pour apporter les dernières retouches aux éléments avant leur rendu.
- Le thread de l'interface graphique est bloqué.
- Le signal ` QQuickWindow::beforeSynchronizing()` est émis. Les applications peuvent se connecter directement (à l’aide de ` Qt::DirectConnection`) à ce signal pour effectuer toute préparation nécessaire avant d’appeler ` QQuickItem::updatePaintNode()`.
- Synchronisation de l’état QML dans le graphe de scène. Cela s’effectue en appelant la fonction QQuickItem::updatePaintNode() sur tous les éléments ayant subi des modifications depuis l’image précédente. C’est le seul moment où les éléments QML et les nœuds du graphe de scène interagissent.
- Le blocage du thread de l’interface graphique est levé.
- Le graphe de scène est rendu :
- Le signal QQuickWindow::beforeRendering() est émis. Les applications peuvent établir des connexions directes (à l’aide de Qt::DirectConnection) à ce signal afin d’utiliser des appels API graphiques personnalisés qui s’empileront alors visuellement sous la scène QML.
- Pour les éléments ayant spécifié ` QSGNode::UsePreprocess`, la fonction ` QSGNode::preprocess()` sera appelée.
- Le moteur de rendu traite les nœuds.
- Le moteur de rendu génère des états et enregistre les appels de dessin pour l’API graphique utilisée.
- Le signal ` QQuickWindow::afterRendering()` est émis. Les applications peuvent établir des connexions directes (à l’aide de ` Qt::DirectConnection`) à ce signal pour émettre des appels d’API graphiques personnalisés qui viendront alors se superposer visuellement à la scène QML.
- L'image est désormais prête. Les tampons sont échangés (OpenGL), ou une commande de présentation est enregistrée et les tampons de commande sont soumis à une file d'attente graphique (Vulkan, Metal). Le signal ` QQuickWindow::frameSwapped()` est émis.
- Pendant que le thread de rendu effectue le rendu, l’interface graphique est libre de faire avancer les animations, de traiter les événements, etc.
Le moteur de rendu multithread est actuellement utilisé par défaut sous Windows avec Direct3D 11 et avec OpenGL lors de l’utilisation de opengl32.dll, sous Linux à l’exception de Mesa llvmpipe, sous macOS avec Metal, sur les plateformes mobiles, sous Linux embarqué avec EGLFS, et avec Vulkan quelle que soit la plateforme. Tout cela est susceptible de changer dans les prochaines versions. Il est toujours possible de forcer l’utilisation du moteur de rendu multithread en définissant la variable d’environnement QSG_RENDER_LOOP=threaded.
Boucle de rendu non multithread (« basic »)
La boucle de rendu non multithread est actuellement utilisée par défaut sous Windows avec OpenGL lorsque l’on n’utilise pas le fichier opengl32.dll standard du système, sous macOS avec OpenGL, WebAssembly et sous Linux avec certains pilotes. Dans ce dernier cas, il s'agit principalement d'une mesure de précaution, car toutes les combinaisons de pilotes OpenGL et de systèmes de fenêtrage n'ont pas été testées.
Sous macOS et OpenGL, la boucle de rendu multithread n’est pas prise en charge lors de la compilation avec Xcode 10 (SDK 10.14) ou une version ultérieure, car cela active les vues basées sur des couches sous macOS 10.14. Vous pouvez effectuer la compilation avec Xcode 9 (SDK 10.13) pour désactiver le support par couches ; dans ce cas, la boucle de rendu multithread est disponible et utilisée par défaut. Il n’existe aucune restriction de ce type avec Metal.
La boucle de rendu multithread n’est pas prise en charge sur WebAssembly, car la plateforme Web offre une prise en charge limitée de l’utilisation de WebGL sur des threads autres que le thread principal, ainsi qu’une prise en charge limitée du blocage du thread principal.
Même lorsque vous utilisez la boucle de rendu non threadée, vous devez écrire votre code comme si vous utilisiez le moteur de rendu threadé, car ne pas le faire rendra le code non portable.
Voici une illustration simplifiée de la séquence de rendu d'une image dans le moteur de rendu non multithread.

Gestion des animations
À quoi correspond l'Advance Animations e dans les schémas ci-dessus ?
Par défaut, une animation « Qt Quick » (telle que NumberAnimation) est pilotée par le pilote d’animation par défaut. Celui-ci s’appuie sur des temporisateurs système de base, tels que QObject::startTimer(). Le temporisateur fonctionne généralement avec un intervalle de 16 millisecondes. Bien que cela ne soit jamais tout à fait précis et dépende également de la précision des temporisateurs de la plateforme sous-jacente, cela présente l’avantage d’être indépendant du rendu. Cela fournit des résultats uniformes, quel que soit le taux de rafraîchissement de l’écran et que la synchronisation avec la synchronisation verticale de l’écran soit active ou non. C’est ainsi que les animations fonctionnent avec la boucle de rendu « basic ».
Afin d’obtenir des résultats plus précis avec moins de saccades à l’écran, indépendamment de la conception de la boucle de rendu (qu’elle soit mono-thread ou multi-thread), une boucle de rendu peut décider d’installer son propre pilote d’ advancing t de prendre en charge elle-même le fonctionnement de l’ t, sans s’appuyer sur des temporisateurs.
C’est ce que met en œuvre la boucle de rendu threaded. En effet, elle installe non pas un, mais deux pilotes d’animation : l’un sur le thread de l’interface graphique (pour gérer les animations classiques, telles que NumberAnimation), et l’autre sur le thread de rendu (pour gérer les animations du thread de rendu, c’est-à-dire les types Animator, tels que OpacityAnimator ou XAnimator). Ces deux processus sont mis à jour lors de la préparation d’une image, c’est-à-dire que les animations sont désormais synchronisées avec le rendu. Cela est logique, car la présentation est limitée par la synchronisation verticale de l’écran via la pile graphique sous-jacente.
Par conséquent, dans le schéma de la boucle de rendu d’ threaded ci-dessus, il existe une étape explicite d’ Advance animations ion sur les deux threads. Pour le thread de rendu, cela est trivial : comme le thread est limité par la synchronisation verticale, faire progresser les animations (pour les types de type « Animator ») à chaque image comme si 16,67 millisecondes s’étaient écoulées donne des résultats plus précis que de se fier à un temporisateur système. (lorsqu’il est limité par la synchronisation verticale, qui est de 1000/60 e de milliseconde avec un taux de rafraîchissement de 60 Hz, on peut raisonnablement supposer que le temps écoulé depuis la dernière opération effectuée pour l’image précédente est approximativement de cette durée)
La même approche fonctionne également pour les animations sur le thread de l’interface graphique (thread principal) : en raison de la synchronisation indispensable des données entre les threads de l’interface graphique et de rendu, le thread de l’interface graphique est en effet limité à la même fréquence que le thread de rendu, tout en bénéficiant d’une charge de travail moindre, ce qui laisse davantage de marge de manœuvre à la logique de l’application, car une grande partie des préparatifs de rendu est désormais déchargée vers le thread de rendu.
Bien que les exemples ci-dessus aient utilisé 60 images par seconde, « Qt Quick » est également conçu pour d’autres fréquences de rafraîchissement : la fréquence est interrogée auprès de l’ QScreen e et de la plateforme. Par exemple, avec un écran à 144 Hz, l’intervalle est de 6,94 ms. C’est précisément ce qui peut poser problème si la limitation basée sur la synchronisation verticale (vsync) ne fonctionne pas comme prévu, car si ce que la boucle de rendu pense qu’il se passe ne correspond pas à la réalité, le rythme de l’animation sera incorrect.
Remarque : à partir de Qt 6.5, la boucle de rendu multithread offre la possibilité d’opter pour un autre pilote d’animation, basé uniquement sur le temps écoulé (QElapsedTimer). Pour activer cette fonctionnalité, définissez la variable d’environnement QSG_USE_SIMPLE_ANIMATION_DRIVER sur une valeur non nulle. Cela présente l'avantage de ne nécessiter aucune infrastructure de repli vers une « QTimer » en cas de fenêtres multiples, de ne pas avoir besoin d’heuristiques pour déterminer si la limitation basée sur le vsync est absente ou défaillante, d’être compatible avec tout type de dérive temporelle dans la limitation par vsync, et de ne pas être lié au taux de rafraîchissement de l’écran principal, ce qui permet potentiellement un meilleur fonctionnement dans les configurations multi-écrans. Cela permet également de gérer correctement les animations du thread de rendu (de type « Animator ») même si la limitation basée sur le vsync est défaillante ou désactivée. En revanche, les animations peuvent sembler moins fluides avec cette approche. Dans un souci de compatibilité, cette fonctionnalité est pour l’instant proposée en option.
En résumé, la boucle de rendu « threaded » devrait offrir des animations plus fluides avec moins de saccades, à condition que les conditions suivantes soient remplies :
- Il n’y a qu’une seule fenêtre (comme dans ` QQuickWindow`) à l’écran.
- La limitation basée sur VSync fonctionne comme prévu avec la pile graphique et d’affichage sous-jacente.
Que se passe-t-il s’il n’y a aucune fenêtre visible ou s’il y en a plus d’une ?
Lorsqu’il n’y a pas de fenêtre pouvant être affichée, par exemple parce que notre QQuickWindow est minimisée (Windows) ou entièrement masquée (macOS), nous ne pouvons pas afficher d’images et ne pouvons donc pas compter sur le fait que le thread « fonctionne » en synchronisation avec la fréquence de rafraîchissement de l’écran. Dans ce cas, la boucle de rendu d’ threaded passe automatiquement à une approche basée sur le temporisateur système pour piloter les animations, c’est-à-dire qu’elle bascule temporairement vers le mécanisme que la boucle d’ basic s utiliserait.
Il en va de même lorsqu’il y a plusieurs instances d’ QQuickWindow à l’écran. Le modèle présenté ci-dessus pour faire progresser les animations sur le thread de l’interface graphique, rendu possible par sa synchronisation avec le thread de rendu, n’est plus satisfaisant, car il existe désormais plusieurs points de synchronisation avec plusieurs threads de rendu (un par fenêtre). Il devient donc également nécessaire de revenir à l’approche basée sur le temporisateur système, car la durée et la fréquence de blocage du thread GUI dépendent désormais d’un certain nombre de facteurs, notamment le contenu des fenêtres (sont-elles animées ? à quelle fréquence sont-elles mises à jour ?) et du comportement de la pile graphique (comment gère-t-elle exactement deux threads ou plus utilisant le « wait-for-vsync » ?). Comme nous ne pouvons pas garantir une limitation à la fréquence de présentation de la fenêtre (de quelle fenêtre s’agirait-il, d’ailleurs ?) de manière stable et multiplateforme, l’avancement des animations ne peut pas être basé sur le rendu.
Ce changement de mécanisme de gestion des animations est transparent pour les applications.
Que se passe-t-il si la limitation basée sur le vsync est défaillante, désactivée globalement, ou si l’application l’a désactivée elle-même ?
La boucle de rendu d'threaded s'appuie sur l'implémentation de l'API graphique et/ou le système de fenêtrage pour la régulation, par exemple en demandant un intervalle de rafraîchissement de 1 dans le cas d'OpenGL (GLX, EGL, WGL), en appelant Present() avec un intervalle de 1 pour Direct 3D, ou en utilisant le mode de présentation « FIFO » avec Vulkan.
Certains pilotes graphiques permettent aux utilisateurs de passer outre ce paramètre et de le désactiver, en ignorant la requête de Qt. C'est notamment le cas d'un panneau de configuration du pilote graphique à l'échelle du système qui permet de remplacer les paramètres de l'application en matière de vsync. Il peut également arriver qu’une pile graphique soit incapable de fournir une régulation correcte basée sur le vsync, ce qui peut être le cas dans certaines machines virtuelles (principalement en raison de l’utilisation d’une implémentation d’OpenGL ou de Vulkan basée sur une rastérisation logicielle).
Sans blocage au niveau de l’opération swap/present (ou d’une autre opération graphique), une telle boucle de rendu ferait progresser les animations trop rapidement. Cela ne poserait aucun problème avec la boucle de rendu « basic », car celle-ci s’appuie toujours sur les temporisateurs système. Avec « threaded », le comportement peut varier en fonction de la version de Qt :
- Si l’on sait qu’un système est incapable de fournir une limitation basée sur la synchronisation verticale (vsync), la seule option avant Qt 6.4 consistait à utiliser la boucle de rendu «
basic», en définissant manuellementQSG_RENDER_LOOP=basicdans l’environnement avant d’exécuter l’application. - À partir de Qt 6.4, le fait de définir la variable d’environnement `
QSG_NO_VSYNC` sur une valeur non nulle, ou de définir la méthode ` QSurfaceFormat::swapInterval()` de la fenêtre sur `0`, permet également de pallier le problème : en demandant explicitement la désactivation du blocage basé sur le vsync, que cette demande ait ou non un effet concret, la boucle de renduthreadedpeut, par extension, reconnaître qu’il est vain de s’appuyer sur le vsync pour piloter les animations, et elle se rabattra alors sur l’utilisation des temporisateurs système, comme elle le ferait pour plusieurs fenêtres. - Mieux encore, à partir de Qt 6.4, le graphe de scène tente également de détecter, à l’aide de quelques heuristiques simples, que les images sont affichées « trop rapidement », et bascule automatiquement vers les temporisateurs système si cela s’avère nécessaire. Cela signifie que, dans la plupart des cas, aucune intervention ne sera nécessaire et que les applications exécuteront les animations comme prévu, même lorsque la boucle de rendu par défaut est celle de l’
threaded. Bien que ce processus soit transparent pour les applications, il est utile, à des fins de dépannage et de développement, de savoir qu’il est consigné par un message «"Window 0x7ffc8489c3d0 is determined to have broken vsync throttling ..."» affiché lorsque l’option «QSG_INFO» ou «qt.scenegraph.general» est activée. Cette méthode présente l’inconvénient de ne s’activer qu’après un petit nombre d’images, car elle doit d’abord collecter des données à analyser ; cela signifie que lors de l’ouverture d’un fichier « QQuickWindow », l’application peut encore afficher des animations trop rapides pendant un court instant. De plus, elle peut ne pas détecter toutes les situations possibles de rupture de synchronisation verticale (vsync).
Notez toutefois que, de par sa conception, aucune de ces solutions n’aide au rendu des animations de thread (les types Animator ). En l’absence de blocage basé sur le vsync, animators avancera de manière incorrecte par défaut, plus rapidement que prévu, même lorsque les solutions de contournement sont activées pour les animations classiques. Si cela pose problème, envisagez d’utiliser le pilote d’animation alternatif en définissant QSG_USE_SIMPLE_ANIMATION_DRIVER.
Remarque : sachez que la logique de la boucle de rendu et le traitement des événements sur le thread de l’interface graphique (thread principal) ne sont pas nécessairement libérés de toute limitation, même si l’attente du vsync est désactivée : les deux boucles de rendu planifient les mises à jour des fenêtres via ` QWindow::requestUpdate()`. Ce comportement s’appuie sur un temporisateur de 5 ms du thread de l’interface graphique sur la plupart des plateformes, afin de laisser le temps nécessaire au traitement des événements. Sur certaines plateformes, par exemple macOS, le système utilise des API spécifiques à la plateforme (telles que CVDisplayLink) pour être informé du moment approprié pour préparer une nouvelle image, probablement lié d’une manière ou d’une autre au vsync de l’écran. Cela peut être pertinent dans le cadre de tests de performances et de situations similaires. Pour les applications et outils cherchant à effectuer des tests de performances de bas niveau, il peut être utile de définir la variable d’environnement ` QT_QPA_UPDATE_IDLE_TIME ` sur ` 0 ` afin de réduire potentiellement le temps d’inactivité sur le thread de l’interface graphique. Pour une utilisation normale des applications, les valeurs par défaut devraient, dans la plupart des cas, être suffisantes.
Remarque : en cas de doute, activez les catégories de journalisation « qt.scenegraph.general » et « qt.scenegraph.time.renderloop » à des fins de dépannage, car celles-ci peuvent fournir des indices expliquant pourquoi le rendu et les animations ne s’exécutent pas au rythme attendu.
Contrôle personnalisé du rendu avec QQuickRenderControl
Lorsque vous utilisez ` QQuickRenderControl`, la gestion de la boucle de rendu est transférée à l’application. Dans ce cas, aucune boucle de rendu intégrée n’est utilisée. C’est donc à l’application qu’il revient d’appeler les étapes de polissage, de synchronisation et de rendu au moment opportun. Il est possible d’implémenter un comportement avec ou sans threads, similaire à ceux présentés ci-dessus.
De plus, les applications peuvent souhaiter implémenter et installer leur propre `QAnimationDriver` en combinaison avec ` QQuickRenderControl`. Cela permet un contrôle total sur le pilotage des animations de ` Qt Quick `, ce qui peut s’avérer particulièrement important pour le contenu qui n’est pas affiché à l’écran, n’ayant aucun rapport avec la fréquence de rafraîchissement, simplement parce qu’il n’y a pas d’affichage de l’image en cours. Cette option est facultative ; par défaut, les animations progressent en fonction du temporisateur système.
Extension du graphe de scène avec un rendu 3D natif et basé sur QRhi
Le graphe de scène propose trois méthodes pour intégrer les commandes graphiques fournies par l’application :
- Émettre des commandes basées sur l’ QRhi, ou des commandes OpenGL, Vulkan, Metal ou Direct3D, directement avant ou après le rendu propre au graphe de scène. Cela revient en fait à préfixer ou à suffixer un ensemble d’appels de dessin au passage de rendu principal. Aucune cible de rendu supplémentaire n’est utilisée.
- Effectuer un rendu vers une texture et créer un nœud texturé dans le graphe de scène. Cela implique un passage de rendu et une cible de rendu supplémentaires.
- Lancer des appels de dessin en ligne avec le rendu propre au graphe de scène en instanciant une sous-classe de ` QSGRenderNode ` dans le graphe de scène. Cette approche est similaire à la première, mais les appels de dessin personnalisés sont effectivement injectés dans le flux de commandes du graphe de scène.
Mode « underlay » / « overlay »
En se connectant aux signaux ` QQuickWindow::beforeRendering()` et ` QQuickWindow::afterRendering()`, les applications peuvent effectuer des appels à l’API 3D native ou à ` QRhi ` directement dans le même contexte que celui utilisé par le graphisme de scène pour le rendu. Avec des API telles que Vulkan ou Metal, les applications peuvent interroger des objets natifs, tels que le tampon de commandes du graphisme de scène, via ` QSGRendererInterface`, et y enregistrer des commandes comme elles le souhaitent. Comme l’indiquent les noms des signaux, l’utilisateur peut alors afficher du contenu soit sous une scène « Qt Quick », soit par-dessus celle-ci. L’avantage de cette intégration réside dans le fait qu’aucune cible de rendu supplémentaire n’est nécessaire pour effectuer le rendu, et qu’une étape de texturation potentiellement coûteuse est éliminée. L’inconvénient est que le rendu personnalisé ne peut être lancé qu’au début ou à la fin du rendu propre à « Qt Quick ». L’utilisation de ` QSGRenderNode ` à la place des signaux ` QQuickWindow ` peut atténuer quelque peu cette restriction, mais dans les deux cas, il convient d’être vigilant en ce qui concerne le contenu 3D et l’utilisation du tampon de profondeur ; en effet, le recours au test de profondeur et au rendu avec l’écriture de profondeur activée peut facilement créer des situations où l’utilisation du tampon de profondeur par le contenu personnalisé et celle par le contenu d’ Qt Quick, entrent en conflit.
À partir de Qt 6.6, les API « QRhi » sont considérées comme semi-publiques, c’est-à-dire mises à la disposition des applications et documentées, bien qu’avec une garantie de compatibilité limitée. Cela permet de créer du code de rendu 2D/3D portable et multiplateforme en utilisant les mêmes abstractions graphiques et de shaders que celles utilisées par le graphe de scène lui-même.
L’exemple « Scene Graph - RHI Under QML » illustre comment implémenter l’approche « underlay/overlay » à l’aide d’ QRhi.
L'exemple « Scene Graph - OpenGL sous QML » montre comment utiliser ces signaux avec OpenGL.
L'exemple « Scene Graph - Direct3D 11 sous QML » montre comment utiliser ces signaux avec Direct3D.
L'exemple « Scene Graph - Metal sous QML » montre comment utiliser ces signaux avec Metal.
L'exemple « Scene Graph - Vulkan Under QML » montre comment utiliser ces signaux avec Vulkan.
À partir de Qt 6.0, toute utilisation directe de l’API graphique sous-jacente doit être encadrée par un appel à ` QQuickWindow::beginExternalCommands()` et ` QQuickWindow::endExternalCommands()`. Ce concept, que vous connaissez peut-être déjà grâce à QPainter::beginNativePainting(), a une fonction similaire : il permet au graphe de scène Qt Quick de reconnaître que tout état mis en cache et toute hypothèse concernant l’état au sein du passage de rendu actuellement enregistré, s’il y en a un, sont désormais invalides, car le code de l’application a pu les modifier en intervenant directement sur l’API graphique sous-jacente. Cela ne s’applique pas et n’est pas nécessaire lorsque vous utilisez QRhi.
Lorsqu’on combine un rendu OpenGL personnalisé avec le graphe de scène, il est important que l’application ne laisse pas le contexte OpenGL dans un état où des tampons sont liés, des attributs activés, ou des valeurs spéciales présentes dans le tampon Z, le tampon de stencil ou tout autre élément similaire. Cela pourrait entraîner un comportement imprévisible.
Le code de rendu personnalisé doit tenir compte des threads, c'est-à-dire qu'il ne doit pas partir du principe qu'il s'exécute sur le thread GUI (principal) de l'application. Lorsqu'elle se connecte aux signaux « QQuickWindow », l'application doit utiliser Qt::DirectConnection et comprendre que les slots connectés sont appelés sur le thread de rendu dédié du graphe de scène, s'il en existe un.
L’approche basée sur les textures
L’alternative basée sur les textures est l’approche la plus flexible lorsque l’application a besoin d’une image 2D « aplatie » d’un rendu 3D personnalisé au sein de la scène Qt Quick. Cela permet également d’utiliser un tampon de profondeur/stencil dédié, indépendant des tampons utilisés par le passage de rendu principal.
Lors de l’utilisation d’OpenGL, la classe de commodité héritée ` QQuickFramebufferObject ` peut être utilisée à cette fin. Les moteurs de rendu personnalisés basés sur ` QRhi` et les API graphiques autres qu’OpenGL peuvent également suivre cette approche, même si ` QQuickFramebufferObject ` ne les prend pas en charge pour le moment. La création et le rendu directs d’une texture à l’aide de l’API sous-jacente, suivis de l’encapsulation et de l’utilisation de cette ressource dans une scène Qt Quick au sein d’un QQuickItem personnalisé, sont illustrés dans les exemples suivants :
Exemplede graphe de scène - Élément de texture RHI.
Scène Graph – Exempled’importation de texture Vulkan.
Arborescence de scène – Exempled'importation de texture Metal.
L’approche en ligne
À l’aide de ` QSGRenderNode `, les appels de dessin personnalisés ne sont pas injectés au début ou à la fin de l’enregistrement du passage de rendu du graphe de scène, mais plutôt pendant le processus de rendu de celui-ci. Pour ce faire, on crée un nœud « QQuickItem » personnalisé basé sur une instance de ` QSGRenderNode`, un nœud du graphe de scène spécialement conçu pour permettre l’émission de commandes graphiques soit via ` QRhi `, soit via une API 3D native telle qu’OpenGL, Vulkan, Metal ou Direct 3D.
L’exemple « Scene Graph - Custom QSGRenderNode » illustre cette approche.
Éléments personnalisés utilisant QPainter
La classe « QQuickItem » fournit une sous-classe, « QQuickPaintedItem », qui permet aux utilisateurs de rendre du contenu à l’aide de « QPainter ».
Avertissement : l’utilisation de ` QQuickPaintedItem ` implique le recours à une surface 2D indirecte pour le rendu de son contenu, soit via une rastérisation logicielle, soit via un objet de tampon de trame OpenGL (FBO) ; le rendu s’effectue donc en deux étapes. Il faut d’abord rastériser la surface, puis la dessiner. L’utilisation directe de l’API du graphe de scène est toujours nettement plus rapide.
Prise en charge de la journalisation
Le graphe de scène prend en charge plusieurs catégories de journalisation. Celles-ci peuvent s'avérer utiles pour identifier à la fois les problèmes de performances et les bogues, en plus d’être d’une grande aide pour les contributeurs Qt.
qt.scenegraph.time.texture- consigne le temps passé à charger les texturesqt.scenegraph.time.compilation- consigne le temps passé à la compilation des shadersqt.scenegraph.time.renderer- consigne le temps passé aux différentes étapes du moteur de renduqt.scenegraph.time.renderloop- consigne le temps passé aux différentes étapes de la boucle de rendu. Avec la boucle de rendu «threaded», cela permet de mieux comprendre le temps écoulé entre les différentes étapes de préparation des images, tant au niveau de l’interface graphique que du thread de rendu. Cela peut donc également constituer un outil de dépannage utile, par exemple pour vérifier comment la limitation basée sur le vsync et d’autres fonctionnalités de bas niveau de Qt, telles que ` QWindow::requestUpdate()`, affectent le pipeline de rendu et d’affichage.qt.scenegraph.time.glyph- enregistre le temps passé à préparer les glyphes du champ de distanceqt.scenegraph.general- consigne des informations générales sur les différentes parties du graphe de scène et de la pile graphiqueqt.scenegraph.renderloop- génère un journal détaillé des différentes étapes impliquées dans le rendu. Ce mode de journalisation est principalement utile pour les développeurs travaillant sur Qt.
La variable d’environnement héritée ` QSG_INFO ` est également disponible. La définir sur une valeur non nulle active la catégorie ` qt.scenegraph.general `.
Remarque : en cas de problèmes graphiques, ou si vous avez un doute sur la boucle de rendu ou l'API graphique utilisée, lancez toujours l'application en activant au minimum qt.scenegraph.general et qt.rhi.*, ou en définissant QSG_INFO=1. Cela permettra d'afficher certaines informations essentielles dans la sortie de débogage lors de l'initialisation.
Backend du graphe de scène
Outre l’API publique, le graphe de scène dispose d’une couche d’adaptation qui permet de réaliser des adaptations spécifiques au matériel. Il s’agit d’une API de plugin interne et privée, non documentée, qui permet aux équipes chargées de l’adaptation matérielle de tirer le meilleur parti de leur matériel. Elle comprend :
- Des textures personnalisées ; plus précisément, l’implémentation de ` QQuickWindow::createTextureFromImage ` et la représentation interne de la texture utilisée par les types ` Image ` et ` BorderImage `.
- Un moteur de rendu personnalisé ; la couche d’adaptation permet au plugin de décider comment le graphe de scène est parcouru et rendu, ce qui permet d’optimiser l’algorithme de rendu pour un matériel spécifique ou d’utiliser des extensions améliorant les performances.
- Implémentation personnalisée du graphe de scène pour de nombreux types QML par défaut, y compris le rendu du texte et des polices.
- Pilote d’animation personnalisé ; permet au système d’animation de s’accrocher au rafraîchissement vertical de l’affichage de bas niveau pour obtenir un rendu fluide.
- Boucle de rendu personnalisée ; permet un meilleur contrôle de la manière dont QML gère les fenêtres multiples.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.