Sur cette page

Qt Quick Graphique 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 que OpenGL ES, OpenGL, Vulkan, Metal ou Direct 3D. L'utilisation d'un graphe de scène pour les graphiques plutôt que les systèmes de peinture impératifs traditionnels (QPainter et similaires) signifie que la scène à rendre peut être conservée entre les images et que l'ensemble complet des primitives à rendre est connu avant le début du rendu. Cela permet un certain nombre d'optimisations, telles que le rendu par lots pour minimiser les changements d'état et l'élimination des primitives obscurcies.

Par exemple, supposons qu'une interface utilisateur contienne une liste de dix éléments où chaque élément a une couleur de fond, une icône et un texte. En utilisant les techniques de dessin traditionnelles, il en résulterait 30 appels de dessin et un nombre similaire de changements d'état. Un graphe de scène, en revanche, pourrait réorganiser les primitives à rendre 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. La mise en lots et la réduction des changements d'état de cette manière peuvent considérablement améliorer les performances sur certains matériels.

Le graphe de scène est étroitement lié à Qt Quick 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 par le biais d'un appel à QQuickItem::updatePaintNode().

Le graphe de scène est une représentation graphique de la scène de l'élément, une structure indépendante qui contient suffisamment d'informations pour rendre tous les éléments. Une fois mis en place, il peut être manipulé et rendu indépendamment de l'état des éléments. Sur de nombreuses plates-formes, le graphique de la 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 énumérées sur cette page sont spécifiques au comportement intégré et par défaut du graphe de scène Qt Quick. Lors de l'utilisation d'une autre adaptation du graphe de scène, telle que l'adaptation software, il se peut que certains concepts ne s'appliquent pas. Pour plus d'informations sur les différentes adaptations du graphe de scène, voir 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ènes", une définition plus précise est "arbre de nœuds". L'arbre est construit à partir des types QQuickItem dans la scène QML et, en interne, la scène est ensuite traitée par un moteur de rendu qui dessine la scène. Les nœuds eux-mêmes ne contiennent pas de code de dessin actif ni de fonction virtuelle paint().

Même si l'arbre des nœuds est principalement construit en interne par les types QML Qt Quick existants, il est possible pour les utilisateurs d'ajouter des sous-arbres complets avec leur propre contenu, y compris des sous-arbres qui représentent des modèles 3D.

Nœuds

Le nœud le plus important pour les utilisateurs est le nœud QSGGeometryNode. Il est utilisé pour définir des graphiques personnalisés en définissant leur géométrie et leur matériau. La géométrie est définie à l'aide de 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 déconnectés 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 quelconque d'enfants et les nœuds géométriques seront rendus de manière à ce qu'ils apparaissent dans l'ordre des enfants, les parents se trouvant derrière leurs enfants.

Remarque : cela ne dit rien sur l'ordre de rendu réel dans le moteur de rendu. Seule la sortie visuelle est garantie.

Les nœuds disponibles sont les suivants

QSGClipNode

Implémente la fonctionnalité d'écrêtage dans le graphe de scène

QSGGeometryNode

Utilisé pour tous les contenus rendus dans le graphe de scène

QSGNode

Classe de base pour tous les nœuds du graphe de scène

QSGOpacityNode

Utilisé pour modifier l'opacité des nœuds

QSGTransformNode

Implémente les transformations dans le graphe de scène

Les nœuds personnalisés sont ajoutés au graphe de scène en sous-classant QQuickItem::updatePaintNode() et en activant le drapeau QQuickItem::ItemHasContents.

Attention : Il est crucial que les opérations graphiques natives (OpenGL, Vulkan, Metal, etc.) et l'interaction avec le graphe de scène se produisent exclusivement sur le thread de rendu, principalement pendant l'appel updatePaintNode(). La règle de base est de n'utiliser que des classes avec le préfixe "QSG" à l'intérieur de la fonction QQuickItem::updatePaintNode().

Pour plus de détails, voir Scene Graph - Custom Geometry.

Prétraitement

Les nœuds disposent d'une fonction virtuelle QSGNode::preprocess(), qui sera appelée avant le rendu du graphique de la scène. Les sous-classes de nœuds peuvent activer l'indicateur QSGNode::UsePreprocess et remplacer la fonction QSGNode::preprocess() pour effectuer la préparation finale de leur nœud. Par exemple, diviser une courbe de Bézier au niveau de détail correct pour le facteur d'échelle actuel ou mettre à jour une section d'une texture.

Propriété du noeud

La propriété des nœuds est attribuée soit explicitement par le créateur, soit par le graphe de la scène en définissant le drapeau QSGNode::OwnedByParent. L'attribution de la propriété au graphe de scène est souvent préférable car elle simplifie le nettoyage lorsque le graphe de scène vit en dehors du fil d'exécution de l'interface graphique.

Matériel

Le matériau décrit comment l'intérieur d'une géométrie dans un site QSGGeometryNode est rempli. Il encapsule les shaders graphiques pour les étapes vertex et fragment du pipeline graphique et offre une grande flexibilité dans ce qui peut être réalisé, bien que la plupart des éléments Qt Quick eux-mêmes n'utilisent que des matériaux très basiques, tels que les remplissages de couleur solide et de texture.

Les utilisateurs qui souhaitent simplement appliquer un ombrage personnalisé à un type d'élément QML peuvent le faire directement en QML à l'aide du type ShaderEffect.

Vous trouverez ci-dessous une liste complète des classes de matériaux :

QSGFlatColorMaterial

Moyen pratique de rendre une géométrie de couleur solide dans le graphe de scène

QSGMaterial

Encapsule l'état de rendu d'un programme de nuanceur

QSGMaterialShader

Représente un programme de nuanceur indépendant de l'API graphique

QSGMaterialType

Utilisé comme jeton de type unique en combinaison avec QSGMaterial

QSGOpaqueTextureMaterial

Façon pratique de rendre une géométrie texturée dans le graphe de scène

QSGTextureMaterial

Façon pratique de rendre la géométrie texturée dans le graphe de scène

QSGVertexColorMaterial

Façon pratique de rendre la géométrie colorée par sommet dans le graphe de scène

Nœuds pratiques

L'API du graphe de scène est de bas niveau et se concentre sur les performances plutôt que sur la commodité. L'écriture de géométries et de 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 comprend quelques classes de commodité qui permettent d'accéder facilement aux nœuds personnalisés les plus courants.

Graphique de scène et rendu

Le rendu du graphe de scène se fait en interne dans la classe QQuickWindow, et il n'y a pas d'API publique pour y accéder. Il y a cependant quelques endroits dans le pipeline de rendu où l'utilisateur peut attacher du code d'application. Celui-ci peut être utilisé pour ajouter un contenu de graphe de scène personnalisé ou pour 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, voir Qt Quick Scene Graph Default Renderer.

Il existe deux variantes de boucle de rendu : basic threaded basic est un thread unique, tandis que threaded effectue le rendu du graphe de scène sur un thread dédié. Qt tente de choisir une boucle appropriée en fonction de la plate-forme et éventuellement des pilotes graphiques utilisés. Lorsque cela 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 la variable qt.scenegraph.general logging category .

Boucle de rendu threadée ("threaded")

Dans de nombreuses configurations, le rendu du graphe de scène s'effectue sur un thread de rendu dédié. Cela permet d'augmenter le parallélisme des processeurs multicœurs et de mieux utiliser les temps d'arrêt tels que l'attente d'un appel bloquant à la mémoire tampon d'échange. Cela permet d'améliorer considérablement les performances, mais impose certaines restrictions quant à l'endroit et au moment où l'interaction avec le graphe de la scène peut avoir lieu.

Voici un aperçu simple de la manière dont une image est rendue avec la boucle de rendu threadée et OpenGL. Les étapes sont les mêmes avec d'autres API graphiques, à l'exception des spécificités du contexte OpenGL.

  1. Un changement se produit dans la scène QML, provoquant l'appel à QQuickItem::update(). Cela peut être le résultat, par exemple, d'une animation ou d'une entrée utilisateur. Un événement est envoyé au processus de rendu pour initier une nouvelle image.
  2. Le thread de rendu se prépare à dessiner une nouvelle image et lance un bloc sur le thread de l'interface graphique.
  3. Pendant que le fil de rendu prépare la nouvelle image, le fil d'interface graphique appelle QQuickItem::updatePolish() pour effectuer les dernières retouches des éléments avant qu'ils ne soient rendus.
  4. Le thread GUI est bloqué.
  5. Le signal QQuickWindow::beforeSynchronizing() est émis. Les applications peuvent établir des connexions directes (à l'aide de Qt::DirectConnection) avec ce signal pour effectuer toute préparation nécessaire avant les appels à QQuickItem::updatePaintNode().
  6. Synchronisation de l'état QML dans le graphe de scène. Cela se fait en appelant la fonction QQuickItem::updatePaintNode() sur tous les éléments qui ont changé 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.
  7. Le bloc de threads de l'interface graphique est libéré.
  8. Le graphe de scène est rendu :
    1. Le signal QQuickWindow::beforeRendering() est émis. Les applications peuvent établir des connexions directes (à l'aide de Qt::DirectConnection) avec ce signal afin d'utiliser des appels d'API graphiques personnalisés qui s'empileront visuellement sous la scène QML.
    2. Les éléments qui ont spécifié QSGNode::UsePreprocess, verront leur fonction QSGNode::preprocess() invoquée.
    3. Le moteur de rendu traite les nœuds.
    4. Le moteur de rendu génère des états et enregistre les appels de dessin pour l'API graphique utilisée.
    5. Le signal QQuickWindow::afterRendering() est émis. Les applications peuvent établir des connexions directes (à l'aide de Qt::DirectConnection) avec ce signal pour émettre des appels d'API graphiques personnalisés qui s'empileront visuellement sur la scène QML.
    6. La trame est maintenant prête. Les tampons sont échangés (OpenGL), ou une commande présente est enregistrée et les tampons de commande sont soumis à une file d'attente graphique (Vulkan, Metal). QQuickWindow::frameSwapped() est émis.
  9. 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 threadé est actuellement utilisé par défaut sous Windows avec Direct3D 11 et avec OpenGL lors de l'utilisation d'opengl32.dll, Linux à l'exclusion de Mesa llvmpipe, macOS avec Metal, les plateformes mobiles et Embedded Linux avec EGLFS, et avec Vulkan quelle que soit la plateforme. Tout cela peut changer dans les prochaines versions. Il est toujours possible de forcer l'utilisation du moteur de rendu threadé en définissant QSG_RENDER_LOOP=threaded dans l'environnement.

Boucle de rendu non threadée ("basic")

La boucle de rendu non threadée est actuellement utilisée par défaut sous Windows avec OpenGL lorsque la dll standard opengl32.dll du système n'est pas utilisée, sous macOS avec OpenGL, WebAssembly, et sous Linux avec certains pilotes. Pour ces derniers, 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.

Sur macOS et OpenGL, la boucle de rendu threadée n'est pas supportée lors de la construction avec XCode 10 (10.14 SDK) ou plus récent, car cela permet d'opter pour des vues en couches sur macOS 10.14. Vous pouvez construire avec Xcode 9 (10.13 SDK) pour ne pas utiliser le layer-backing, auquel cas la boucle de rendu threadée est disponible et utilisée par défaut. Il n'y a pas de restriction de ce type avec Metal.

La boucle de rendu threadée n'est pas supportée par WebAssembly, puisque la plateforme web a un support limité pour l'utilisation de WebGL sur d'autres threads que le thread principal, et un support limité pour le 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 si vous ne le faites pas, votre code ne sera pas portable.

Voici une illustration simplifiée de la séquence de rendu d'une image dans le moteur de rendu non threadé.

Animations de conduite

À quoi fait référence Advance Animations dans les diagrammes 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 les minuteries de base du système, telles que QObject::startTimer(). La minuterie fonctionne généralement avec un intervalle de 16 millisecondes. Bien que cette méthode ne soit jamais totalement précise et qu'elle dépende également de la précision des minuteries de la plateforme sous-jacente, elle présente l'avantage d'être indépendante du rendu. Il fournit des résultats uniformes quel que soit le taux de rafraîchissement de l'écran et si la synchronisation avec la synchronisation verticale de l'écran est 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 simple ou multiple), une boucle de rendu peut décider d'installer son propre pilote d'animation personnalisé et de prendre en charge l'opération advancing, sans s'appuyer sur des temporisateurs.

C'est ce que la boucle de rendu threaded met en œuvre. En fait, elle installe non pas un, mais deux pilotes d'animation : un sur le thread gui (pour piloter les animations régulières, telles que NumberAnimation), et un sur le thread de rendu (pour piloter les animations du thread de rendu, c'est-à-dire les types Animator, tels que OpacityAnimator ou XAnimator). Tous deux sont avancés pendant la préparation d'une image, c'est-à-dire que les animations sont désormais synchronisées avec le rendu. Cela s'explique par le fait que la présentation est ralentie par la synchronisation verticale de l'écran par la pile graphique sous-jacente.

Par conséquent, dans le diagramme de la boucle de rendu threaded ci-dessus, il y a une étape Advance animations explicite sur les deux threads. Pour le thread de rendu, c'est trivial : comme le thread est ralenti par vsync, l'avancement des animations (pour les types Animator ) dans chaque image comme si 16,67 millisecondes s'étaient écoulées donne des résultats plus précis que si l'on se fie à un chronomètre système. (lorsque le thread est réglé sur le timing vsync, qui est de 1000/60 millisecondes avec un taux de rafraîchissement de 60 Hz, on peut supposer qu'il s'est écoulé à peu près autant de temps depuis que la même opération a été effectuée pour l'image précédente).

La même approche fonctionne également pour les animations sur le thread gui (principal) : en raison de la synchronisation essentielle des données entre les threads gui et de rendu, le thread gui est effectivement ralenti au même rythme que le thread de rendu, tout en ayant l'avantage d'avoir moins de travail à faire, ce qui laisse plus de marge de manœuvre pour la logique de l'application puisque la plupart des préparations de rendu sont maintenant déchargées sur le thread de rendu.

Bien que les exemples ci-dessus utilisent 60 images par seconde, Qt Quick est également prêt pour d'autres taux de rafraîchissement : le taux est demandé à QScreen et à la plateforme. Par exemple, avec un écran de 144 Hz, l'intervalle est de 6,94 ms. En même temps, c'est exactement ce qui peut causer des problèmes si l'étranglement basé sur vsync ne fonctionne pas comme prévu, parce que si ce que la boucle de rendu pense qu'il se passe ne correspond pas à la réalité, un rythme d'animation incorrect se produira.

Note : A partir de Qt 6.5, la boucle de rendu threadée offre la possibilité d'opter pour un autre pilote d'animation, en se basant uniquement sur le temps écoulé (QElapsedTimer). Pour activer cette possibilité, réglez la variable d'environnement QSG_USE_SIMPLE_ANIMATION_DRIVER sur une valeur non nulle. Cela présente l'avantage de ne pas nécessiter d'infrastructure pour revenir à QTimer lorsqu'il y a plusieurs fenêtres, de ne pas avoir besoin d'heuristique pour essayer de déterminer si la limitation basée sur vsync est manquante ou cassée, d'être compatible avec n'importe quel type de dérive temporelle dans la limitation vsync, et de ne pas être lié au taux de rafraîchissement de l'écran principal, ce qui peut donc fonctionner mieux dans les configurations multi-écrans. Il gère également les animations du fil de rendu (les types Animator ) correctement, même si l'étranglement basé sur vsync est cassé ou désactivé. D'un autre côté, les animations peuvent être perçues comme moins fluides avec cette approche. Dans un souci de compatibilité, cette fonctionnalité est proposée en option pour le moment.

En résumé, la boucle de rendu threaded devrait fournir des animations plus fluides et moins saccadées si les conditions suivantes sont remplies :

  • Il y a exactement une fenêtre (comme dans QQuickWindow) à l'écran.
  • L'étranglement basé sur VSync fonctionne comme prévu avec la pile graphique et d'affichage underyling.

Que se passe-t-il s'il n'y a pas de fenêtre visible ou s'il y en a plus d'une ?

Lorsqu'il n'y a pas de fenêtre visible, par exemple parce que notre site QQuickWindow est minimisé (Windows) ou complètement masqué (macOS), nous ne pouvons pas présenter d'images, et donc nous ne pouvons pas compter sur le fait que le thread "fonctionne" au même rythme que le taux de rafraîchissement de l'écran. Dans ce cas, la boucle de rendu threaded bascule automatiquement vers une approche basée sur un timer système pour piloter les animations, c'est-à-dire qu'elle bascule temporairement vers le mécanisme que la boucle basic utiliserait.

Il en va de même lorsqu'il y a plus d'une instance QQuickWindow à l'écran. Le modèle présenté ci-dessus pour l'avancement des animations sur le thread de l'interface graphique, activé par sa synchronisation avec le thread de rendu, n'est plus satisfaisant, car il y a maintenant plusieurs points de synchronisation avec plusieurs threads de rendu, (un par fenêtre.) Ici, la chute d'un objet dans une fenêtre de l'interface graphique est un exemple de l'utilisation de l'interface graphique. (Ici, le retour à l'approche basée sur le timer du système devient également nécessaire, car la durée et la fréquence de blocage du thread gui dépendent maintenant d'un certain nombre de facteurs, y compris le contenu des fenêtres (sont-elles animées ? à quelle fréquence sont-elles mises à jour ?) et le comportement de la pile graphique (comment gère-t-elle exactement deux threads ou plus qui se présentent avec la fonction wait-for-vsync ?) Comme nous ne pouvons pas garantir que la vitesse de présentation de la fenêtre (de quelle fenêtre s'agit-il pour commencer ?) soit 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 l'étranglement basé sur vsync est dysfonctionnel, désactivé globalement, ou si l'application l'a elle-même désactivé ?

La boucle de rendu threaded dépend de l'implémentation de l'API graphique et/ou du système de fenêtrage pour l'étranglement, par exemple en demandant un intervalle de swap 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 d'outrepasser ce paramètre et de le désactiver, en ignorant la demande de Qt. Un exemple de cela serait un panneau de contrôle du pilote graphique à l'échelle du système qui permet de remplacer les paramètres de l'application en ce qui concerne vsync. Il peut également arriver qu'une pile graphique soit incapable de fournir un étranglement correct basé sur 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 la rastérisation logicielle).

Sans blocage de l'opération swap/present (ou d'une autre opération graphique), une telle boucle de rendu ferait avancer les animations trop rapidement. Ce n'est pas un problème avec la boucle de rendu de basic, car elle s'appuie toujours sur les temporisateurs du système. Avec threaded, le comportement peut varier en fonction de la version de Qt :

  • Si un système est connu pour ne pas être en mesure de fournir un étranglement basé sur vsync, la seule option avant Qt 6.4 était d'utiliser la boucle de rendu basic, en définissant manuellement QSG_RENDER_LOOP=basic dans 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 à une valeur non nulle ou la valeur de la fenêtre QSurfaceFormat::swapInterval() à 0 peut également résoudre le problème : en demandant explicitement la désactivation du blocage basé sur vsync, même si cette demande n'a aucun effet dans la pratique, la boucle de rendu threaded peut par extension reconnaître qu'il est futile de s'appuyer sur vsync pour piloter les animations, et elle reviendra à l'utilisation des temporisateurs du système, comme elle le ferait pour plus d'une fenêtre.
  • Mieux encore, à partir de Qt 6.4, le graphe de scène tente également de reconnaître, à l'aide de quelques heuristiques simples, que les images sont présentées "trop rapidement", et bascule automatiquement sur les temporisations du système si cela s'avère nécessaire. Cela signifie que dans la plupart des cas, il ne sera pas nécessaire de faire quoi que ce soit et que les applications exécuteront les animations comme prévu, même si la boucle de rendu par défaut est celle de threaded. Bien que cela soit transparent pour les applications, il est utile, à des fins de dépannage et de développement, de savoir que cela est consigné dans un message "Window 0x7ffc8489c3d0 is determined to have broken vsync throttling ..." qui s'affiche lorsque QSG_INFO ou qt.scenegraph.general est activé. Cette méthode présente l'inconvénient de ne s'activer qu'après un petit nombre d'images, étant donné qu'elle doit d'abord collecter des données à évaluer, ce qui signifie qu'à l'ouverture d'un site QQuickWindow, l'application peut encore afficher des animations trop rapides pendant un court laps de temps. En outre, il se peut qu'elle ne prenne pas en compte toutes les situations possibles de rupture de vsync.

Rappelez-vous cependant que, par conception, rien de tout cela n'aide à rendre les animations de threads (les types Animator ). En l'absence de blocage basé sur vsync, animators avancera incorrectement par défaut, plus vite que prévu, même lorsque les solutions de contournement sont activées pour animations. Si cela devient un 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 (principal) ne sont pas nécessairement non accélérés même si l'attente de vsync est désactivée : les deux boucles de rendu planifient les mises à jour pour les fenêtres par l'intermédiaire de QWindow::requestUpdate(). Sur la plupart des plates-formes, ce processus est soutenu par un minuteur de 5 ms pour le fil d'exécution de l'interface graphique, afin de laisser du temps pour le traitement des événements. Sur certaines plateformes, par exemple macOS, il utilise des API spécifiques à la plateforme (telles que CVDisplayLink) pour être informé du moment approprié pour préparer une nouvelle image, probablement liée à la vsync de l'écran sous une forme ou une autre. Cela peut s'avérer utile dans le cadre d'analyses comparatives et de situations similaires. Pour les applications et les outils qui tentent d'effectuer des analyses comparatives de bas niveau, il peut être intéressant de définir la variable d'environnement QT_QPA_UPDATE_IDLE_TIME sur 0 afin de réduire potentiellement le temps d'inactivité du thread de l'interface graphique. Pour une utilisation normale de l'application, 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 pour le dépannage, car elles peuvent révéler des indices sur la raison pour laquelle le rendu et les animations ne s'exécutent pas au rythme prévu.

Contrôle personnalisé du rendu avec QQuickRenderControl

Lors de l'utilisation de QQuickRenderControl, la responsabilité du pilotage 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. Au lieu de cela, c'est à l'application d'invoquer les étapes de polissage, de synchronisation et de rendu au moment opportun. Il est possible de mettre en œuvre un comportement threadé ou non threadé similaire à ceux présentés ci-dessus.

En outre, les applications peuvent souhaiter mettre en œuvre et installer leur propre QAnimationDriver en combinaison avec QQuickRenderControl. Cela donne un contrôle total sur le pilotage des animations Qt Quick, ce qui peut être particulièrement important pour le contenu qui n'est pas affiché à l'écran, n'ayant aucun rapport avec le taux de présentation simplement parce qu'il n'y a pas de présentation de l'image qui se produit. Cette option est facultative ; par défaut, les animations avancent en fonction de la minuterie du système.

Extension du graphe de scène avec le rendu 3D natif et basé sur QRhi

Le graphe de scène offre trois méthodes pour intégrer les commandes graphiques fournies par les applications :

  • L'émission de commandes basées sur QRhi ou de commandes OpenGL, Vulkan, Metal, Direct3D directement avant ou après le propre rendu du graphe de scène. Cela a pour effet d'ajouter ou de rajouter un ensemble d'appels de dessin dans la passe de rendu principale. Aucune cible de rendu supplémentaire n'est utilisée.
  • Rendu vers une texture et création d'un nœud texturé dans le graphe de scène. Cela implique une passe de rendu et une cible de rendu supplémentaires.
  • Émettre des appels de dessin en ligne avec le propre rendu du graphe de scène en instanciant une sous-classe 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 superposition/superposition

En se connectant aux signaux QQuickWindow::beforeRendering() et QQuickWindow::afterRendering(), les applications peuvent effectuer des appels à QRhi ou à l'API 3D native directement dans le même contexte que celui dans lequel le graphe de scène effectue le rendu. Avec des API telles que Vulkan ou Metal, les applications peuvent interroger des objets natifs, tels que le tampon de commande du graphe de scène, via QSGRendererInterface, et y enregistrer des commandes comme bon leur semble. Comme l'indiquent les noms des signaux, l'utilisateur peut alors rendre le contenu soit sous une scène Qt Quick, soit par-dessus. L'avantage d'une telle intégration est qu'aucune cible de rendu supplémentaire n'est nécessaire pour effectuer le rendu, et qu'une étape de texturation éventuellement coûteuse est éliminée. L'inconvénient est que le rendu personnalisé ne peut être effectué qu'au début ou à la fin du propre rendu de Qt Quick. L'utilisation de QSGRenderNode au lieu des signaux de QQuickWindow peut lever quelque peu cette restriction, mais dans les deux cas, il faut faire attention au contenu 3D et à l'utilisation du tampon de profondeur, car le fait de s'appuyer sur le test de profondeur et le rendu avec l'écriture de profondeur activée peut facilement créer des situations où le contenu personnalisé et l'utilisation du tampon de profondeur du contenu de Qt Quick entrent en conflit l'un avec l'autre.

À partir de Qt 6.6, les API QRhi sont considérées comme semi-publiques, c'est-à-dire offertes aux applications et documentées, bien qu'avec une garantie de compatibilité limitée. Cela permet de créer un 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 montre comment mettre en œuvre l'approche underlay/overlay à l'aide de QRhi.

L'exemple Scene Graph - OpenGL Under QML montre comment utiliser ces signaux avec OpenGL.

Le graphique de la scène - Direct3D 11 sous QML donne un exemple sur la manière d'utiliser ces signaux avec Direct3D.

Le graphique de la scène - Metal Sous l'exemple QML donne un exemple sur la façon d'utiliser ces signaux en utilisant Metal.

L'exemple Scene Graph - Vulkan Under QML donne un exemple d'utilisation de ces signaux avec Vulkan.

À partir de Qt 6.0, l'utilisation directe de l'API graphique sous-jacente doit être entourée d'un appel à QQuickWindow::beginExternalCommands() et QQuickWindow::endExternalCommands(). Ce concept peut être familier avec QPainter::beginNativePainting(), et sert un objectif similaire : il permet au graphique de scène Qt Quick de reconnaître que tout état mis en cache et les hypothèses sur l'état dans la passe de rendu actuellement enregistrée, s'il y en a une, sont maintenant invalides, parce que le code de l'application peut l'avoir modifié en travaillant directement avec l'API graphique sous-jacente. Ceci n'est pas applicable et nécessaire lors de l'utilisation de QRhi.

Lorsque l'on mélange 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 avec des tampons liés, des attributs activés, des valeurs spéciales dans le z-buffer ou le stencil-buffer ou similaires. Cela peut entraîner un comportement imprévisible.

Le code de rendu personnalisé doit tenir compte des threads, c'est-à-dire qu'il ne doit pas supposer être exécuté sur le thread GUI (principal) de l'application. Lors de la connexion aux signaux QQuickWindow, l'application doit utiliser Qt::DirectConnection et comprendre que les slots connectés sont invoqués sur le thread de rendu dédié au graphe de scène, s'il y en a 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é dans la scène Qt Quick. Cela permet également d'utiliser un tampon de profondeur/stencil dédié qui est indépendant des tampons utilisés par la passe de rendu principale.

Lorsqu'on utilise OpenGL, la classe de commodité héritée QQuickFramebufferObject peut être utilisée pour réaliser cela. QRhi Les moteurs de rendu personnalisés et les API graphiques autres qu'OpenGL peuvent également suivre cette approche, même si QQuickFramebufferObject ne les prend pas en charge actuellement. La création et le rendu d'une texture directement avec l'API sous-jacente, suivis de l'enveloppement et de l'utilisation de cette ressource dans une scène Qt Quick dans un QQuickItem personnalisé, sont démontrés dans les exemples suivants :

Graphique de scène - Exemple d'élément de texture RHI.

Graphique de scène - exemple d'importation de texture Vulkan.

Graphique de scène - exemple d'importation de textures métalliques.

L'approche en ligne

En utilisant QSGRenderNode, les appels de dessin personnalisés sont injectés non pas au début ou à la fin de l'enregistrement de la passe de rendu du graphe de scène, mais plutôt pendant le processus de rendu du graphe de scène. Ceci est réalisé en créant un QQuickItem personnalisé basé sur une instance de QSGRenderNode, un nœud de graphe de scène qui existe spécifiquement pour permettre l'émission de commandes graphiques via QRhi ou une API 3D native telle que OpenGL, Vulkan, Metal, ou Direct 3D.

L'exemple Scene Graph - Custom QSGRenderNode donne une démonstration de cette approche.

Éléments personnalisés utilisant QPainter

La classe QQuickItem fournit une sous-classe, QQuickPaintedItem, qui permet aux utilisateurs de rendre le contenu à l'aide de QPainter.

Attention : L'utilisation de QQuickPaintedItem utilise une surface 2D indirecte pour rendre son contenu, soit en utilisant une rastérisation logicielle, soit en utilisant un objet framebuffer (FBO) OpenGL, de sorte que le rendu est une opération en deux étapes. Le rendu est donc une opération en deux étapes. Il faut d'abord rastériser la surface, puis la dessiner. L'utilisation directe de l'API de graphe de scène est toujours beaucoup plus rapide.

Prise en charge de la journalisation

Le graphe de scène prend en charge un certain nombre de catégories de journalisation. Celles-ci peuvent être utiles pour traquer les problèmes de performance et les bogues, en plus d'être utiles aux contributeurs de Qt Help.

  • qt.scenegraph.time.texture - enregistre le temps passé à télécharger des textures
  • qt.scenegraph.time.compilation - enregistre le temps passé à compiler les shaders
  • qt.scenegraph.time.renderer - enregistre le temps passé dans les différentes étapes du moteur de rendu
  • qt.scenegraph.time.renderloop - enregistre le temps passé dans les différentes étapes de la boucle de rendu. Avec la boucle de rendu threaded, cela donne un aperçu du temps écoulé entre les différentes étapes de préparation des images à la fois dans l'interface graphique et dans le fil de rendu. Il peut donc également s'agir d'un outil de dépannage utile, par exemple pour confirmer la façon dont l'étranglement basé sur vsync et d'autres activateurs Qt de bas niveau, tels que QWindow::requestUpdate(), affectent le pipeline de rendu et de présentation.
  • qt.scenegraph.time.glyph - enregistre le temps passé à préparer les glyphes du champ de distance
  • qt.scenegraph.general - enregistre des informations générales sur diverses parties du graphe de scène et de la pile graphique
  • qt.scenegraph.renderloop - crée un journal détaillé des différentes étapes du rendu. Ce mode de journalisation est principalement utile pour les développeurs travaillant sur Qt.

La variable d'environnement QSG_INFO est également disponible. En lui attribuant une valeur non nulle, vous activez la catégorie qt.scenegraph.general.

Remarque : lorsque vous rencontrez des problèmes graphiques, ou en cas de doute sur la boucle de rendu ou l'API graphique utilisée, démarrez toujours l'application avec au moins qt.scenegraph.general et qt.rhi.* activés, ou QSG_INFO=1 activé. Cela permettra d'imprimer certaines informations essentielles sur la sortie de débogage lors de l'initialisation.

Backend du graphe de scène

En plus de l'API publique, le graphe de scène possède une couche d'adaptation qui permet à l'implémentation d'effectuer des adaptations spécifiques au matériel. Il s'agit d'une API non documentée, interne et privée, qui permet aux équipes d'adaptation matérielle de tirer le meilleur parti de leur matériel. Elle comprend

  • Des textures personnalisées ; en particulier 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 qui améliorent 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 afin d'obtenir un rendu fluide.
  • Boucle de rendu personnalisée ; permet de mieux contrôler 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.