Qt Quick Rendu par défaut du graphe de scène
Ce document explique le fonctionnement interne du moteur de rendu par défaut du graphe de scène, afin de permettre aux développeurs d’écrire du code qui l’utilise de manière optimale, tant en termes de performances que de fonctionnalités.
Il n’est pas nécessaire de comprendre le fonctionnement interne du moteur de rendu pour obtenir de bonnes performances. Cependant, cela peut s’avérer utile lors de l’intégration avec le graphe de scène ou pour comprendre pourquoi il n’est pas possible de tirer le maximum d’efficacité de la puce graphique.
Remarque : même dans le cas où chaque image est unique et où tout est chargé à partir de zéro, le moteur de rendu par défaut fonctionnera correctement.
Les éléments « Qt Quick » d’une scène QML alimentent une arborescence d’instances « QSGNode ». Une fois créée, cette arborescence constitue une description complète de la manière dont une image donnée doit être rendue. Elle ne contient aucune référence vers les éléments « Qt Quick » et, sur la plupart des plateformes, sera traitée et rendue dans un thread distinct. Le moteur de rendu est une partie autonome du graphe de scène qui parcourt l’arborescence QSGNode et utilise la géométrie définie dans QSGGeometryNode ainsi que l’état de shader défini dans QSGMaterial pour mettre à jour l’état graphique et générer des appels de dessin.
Si nécessaire, le moteur de rendu peut être entièrement remplacé à l’aide de l’API interne du back-end de l’arborescence de scène. Cela présente surtout un intérêt pour les fournisseurs de plateformes qui souhaitent tirer parti de fonctionnalités matérielles non standard. Dans la plupart des cas d’utilisation, le moteur de rendu par défaut sera suffisant.
Le moteur de rendu par défaut s'appuie sur deux stratégies principales pour optimiser le rendu : le regroupement des appels de dessin et la conservation de la géométrie sur le GPU.
Regroupement
Alors qu’une API 2D traditionnelle, telle que QPainter, Cairo ou Context2D, est conçue pour gérer des milliers d’appels de dessin individuels par image, OpenGL et d’autres API accélérées par le matériel offrent de meilleures performances lorsque le nombre d’appels de dessin est très faible et que les changements d’état sont réduits au minimum.
Remarque : bien que OpenGL soit utilisé à titre d’exemple dans les sections suivantes, les mêmes concepts s’appliquent également à d’autres API graphiques.
Considérons le cas d’utilisation suivant :

La manière la plus simple de dessiner cette liste consiste à procéder cellule par cellule. Tout d’abord, l’arrière-plan est dessiné. Il s’agit d’un rectangle d’une couleur spécifique. En termes d’OpenGL, cela implique de sélectionner un programme de shader pour effectuer des remplissages de couleur unie, de définir la couleur de remplissage, de configurer la matrice de transformation contenant les décalages x et y, puis d’utiliser par exemple glDrawArrays pour dessiner les deux triangles formant le rectangle. L’icône est ensuite dessinée. En termes d’OpenGL, cela implique de sélectionner un programme de shader pour dessiner des textures, de choisir la texture active à utiliser, de définir la matrice de transformation, d’activer le mélange alpha, puis d’utiliser par exemple glDrawArrays pour dessiner les deux triangles formant le rectangle de délimitation de l’icône. Le texte et la ligne de séparation entre les cellules suivent un schéma similaire. Et ce processus est répété pour chaque cellule de la liste ; ainsi, pour une liste plus longue, la surcharge imposée par les changements d’état OpenGL et les appels de dessin l’emporte largement sur l’avantage que pourrait apporter l’utilisation d’une API à accélération matérielle.
Lorsque chaque primitive est volumineuse, cette surcharge est négligeable, mais dans le cas d’une interface utilisateur classique, les nombreux petits éléments s’additionnent pour former une surcharge considérable.
Le moteur de rendu par défaut du graphe de scène fonctionne dans le cadre de ces contraintes et s’efforce de regrouper les primitives individuelles en lots tout en conservant exactement le même résultat visuel. Il en résulte une réduction des changements d’état OpenGL et un nombre minimal d’appels de dessin, ce qui se traduit par des performances optimales.
Primitives opaques
Le moteur de rendu fait la distinction entre les primitives opaques et celles qui nécessitent un mélange alpha. En utilisant le tampon Z d’OpenGL et en attribuant à chaque primitive une position Z unique, le moteur de rendu peut réorganiser librement les primitives opaques sans tenir compte de leur emplacement à l’écran ni des autres éléments avec lesquels elles se superposent. En analysant l’état du matériau de chaque primitive, le moteur de rendu crée des lots opaques. D’après l’ensemble d’éléments de base d’ Qt Quick, cela inclut les éléments de type « Rectangle » aux couleurs opaques et les images entièrement opaques, telles que les fichiers JPEG ou BMP.
Un autre avantage de l’utilisation de primitives opaques est qu’elles ne nécessitent pas l’activation de l’ GL_BLEND, ce qui peut s’avérer assez coûteux en ressources, en particulier sur les GPU mobiles et embarqués.
Les primitives opaques sont rendues de l’avant vers l’arrière lorsque l’ glDepthMask et l’ GL_DEPTH_TEST sont activés. Sur les GPU qui effectuent en interne des vérifications « early-z », cela signifie que le shader de fragment n’a pas besoin de s’exécuter pour les pixels ou les blocs de pixels masqués. Attention : le moteur de rendu doit tout de même prendre ces nœuds en compte et le shader de sommets s’exécute toujours pour chaque sommet de ces primitives. Ainsi, si l’application sait qu’un élément est entièrement masqué, il est préférable de le masquer explicitement à l’aide de ` Item::visible ` ou ` Item::opacity`.
Remarque : l’ Item::z sert à contrôler l’ordre d’empilement d’un élément par rapport à ses frères. Il n’a aucun rapport direct avec le moteur de rendu ni avec le tampon Z d’OpenGL.
Primitives avec mélange alpha
Une fois les primitives opaques dessinées, le moteur de rendu désactive l’ glDepthMask, active le GL_BLEND et rend toutes les primitives avec mélange alpha de l’arrière vers l’avant.
Le regroupement des primitives avec mélange alpha nécessite un peu plus d’effort de la part du moteur de rendu, car les éléments qui se chevauchent doivent être rendus dans le bon ordre pour que le mélange alpha s’affiche correctement. Se fier uniquement au tampon Z ne suffit pas. Le moteur de rendu effectue un passage sur toutes les primitives avec mélange alpha et examine leur rectangle de délimitation ainsi que l’état de leur matériau afin de déterminer quels éléments peuvent être regroupés et lesquels ne le peuvent pas.

Dans le cas le plus à gauche, les arrière-plans bleus peuvent être dessinés en un seul appel et les deux éléments de texte en un autre appel, car les textes ne se superposent qu’à un arrière-plan devant lequel ils sont empilés. Dans le cas le plus à droite, l’arrière-plan de « Item 4 » chevauche le texte de « Item 3 » ; dans ce cas, chacun des arrière-plans et des textes doit donc être dessiné à l’aide d’appels distincts.
Sur l’axe Z, les primitives alpha sont entrelacées avec les nœuds opaques et peuvent déclencher l’« early-z » lorsqu’il est disponible, mais là encore, définir « Item::visible » sur « false » est toujours plus rapide.
Combinaison avec des primitives 3D
Le graphe de scène prend en charge les primitives pseudo-3D et les primitives 3D à part entière. Par exemple, on peut implémenter un effet de « page qui se recourbe » à l’aide d’un ` ShaderEffect ` ou créer un tore avec bump mapping à l’aide de ` QSGGeometry ` et d’un matériau personnalisé. Ce faisant, il faut tenir compte du fait que le moteur de rendu par défaut utilise déjà le tampon de profondeur.
Le moteur de rendu modifie le shader de sommets renvoyé par QSGMaterialShader::vertexShader(), compresse les valeurs z des sommets après l'application des matrices de modèle-vue et de projection, puis ajoute une légère translation sur l'axe z afin de les positionner correctement.
La compression part du principe que les valeurs z sont comprises entre 0 et 1.
Atlas de textures
La texture active correspond à un état OpenGL unique, ce qui signifie que plusieurs primitives utilisant des textures OpenGL différentes ne peuvent pas être regroupées en lots. Le graphe de scène d’ Qt Quick, pour cette raison, permet d’allouer plusieurs instances d’ QSGTexture s en tant que sous-régions plus petites d’une texture plus grande : un atlas de textures.
Le principal avantage des atlas de textures réside dans le fait que plusieurs instances d’« QSGTexture » font désormais référence à la même instance de texture OpenGL. Cela permet également de regrouper par lots les appels de dessin texturés, tels que les éléments « Image », « BorderImage », « ShaderEffect », ainsi que les types C++ tels que « QSGSimpleTextureNode » et les nœuds « QSGGeometryNodes » personnalisés utilisant des textures.
Remarque : les texturesvolumineuses ne sont pas intégrées à l’atlas de textures.
Les textures basées sur un atlas sont créées en transmettant QQuickWindow::TextureCanUseAtlas à la fonction QQuickWindow::createTextureFromImage().
Remarque : les textures baséessur un atlas n’ont pas de coordonnées de texture comprises entre 0 et 1. Utilisez la fonction ` QSGTexture::normalizedTextureSubRect()` pour obtenir les coordonnées de texture de l’atlas.
Le graphe de scène utilise des heuristiques pour déterminer la taille que doit avoir l’atlas et le seuil de taille à partir duquel une texture est intégrée à l’atlas. Si des valeurs différentes sont nécessaires, il est possible de les redéfinir à l’aide des variables d’environnement QSG_ATLAS_WIDTH=[width], QSG_ATLAS_HEIGHT=[height] et QSG_ATLAS_SIZE_LIMIT=[size]. La modification de ces valeurs intéressera principalement les éditeurs de plateformes.
Regroupement des racines
Outre le regroupement des primitives compatibles en lots, le moteur de rendu par défaut s’efforce également de minimiser la quantité de données à envoyer au GPU pour chaque image. Le moteur de rendu par défaut identifie les sous-arborescences qui vont ensemble et tente de les regrouper dans des lots distincts. Une fois les lots identifiés, ils sont fusionnés, transférés et stockés dans la mémoire du GPU à l’aide d’objets de tampon de sommets (Vertex Buffer Objects).
Nœuds de transformation
Chaque élément « Qt Quick » insère un « QSGTransformNode » dans l’arborescence du graphe de scène afin de gérer ses coordonnées x, y, son échelle ou sa rotation. Les éléments enfants seront placés sous ce nœud de transformation. Le moteur de rendu par défaut suit l’état des nœuds de transformation d’une image à l’autre et examine les sous-arborescences pour déterminer si un nœud de transformation est susceptible de devenir la racine d’un ensemble de lots. Un nœud de transformation qui change d’une image à l’autre et qui possède une sous-arborescence assez complexe peut devenir la racine d’un lot.
Les nœuds QSGGeometryNodes situés dans la sous-arborescence d’une racine de lot sont pré-transformés par rapport à la racine sur le CPU. Ils sont ensuite transférés et conservés sur le GPU. Lorsque la transformation change, le moteur de rendu n’a qu’à mettre à jour la matrice de la racine, et non chaque élément individuellement, ce qui rend le défilement des listes et des grilles très rapide. Pour les images successives, tant qu’aucun nœud n’est ajouté ou supprimé, le rendu de la liste est pratiquement gratuit. Lorsqu’un nouveau contenu entre dans le sous-arbre, le lot qui le contient est reconstruit, mais cette opération reste relativement rapide. Il y a généralement plusieurs images inchangées pour chaque image comportant des nœuds ajoutés ou supprimés lors d’un défilement dans une grille ou une liste.
Un autre avantage de l’identification des nœuds de transformation comme racines de lot est que cela permet au moteur de rendu de conserver les parties de l’arborescence qui n’ont pas changé. Par exemple, supposons qu’une interface utilisateur se compose d’une liste et d’une rangée de boutons. Lorsque la liste défile et que des délégués sont ajoutés ou supprimés, le reste de l’interface, à savoir la rangée de boutons, reste inchangé et peut être dessiné à l’aide de la géométrie déjà stockée sur le GPU.
Les seuils de nœuds et de sommets permettant à un nœud de transformation de devenir une racine de lot peuvent être redéfinis à l’aide des variables d’environnement QSG_RENDERER_BATCH_NODE_THRESHOLD=[count] et QSG_RENDERER_BATCH_VERTEX_THRESHOLD=[count]. La redéfinition de ces indicateurs sera principalement utile aux éditeurs de plateformes.
Remarque : sous une racine de lot, un lot est créé pour chaque ensemble unique d’état de matériau et de type de géométrie.
Découpage
Lorsque Item::clip est défini sur true, cela crée un QSGClipNode dont la géométrie est un rectangle. Le moteur de rendu par défaut appliquera ce découpage en utilisant la technique du « scissoring » dans OpenGL. Si l’élément est pivoté d’un angle autre que 90 degrés, le tampon de stencil d’OpenGL est utilisé. Qt Quick L’élément ne prend en charge la définition d’un rectangle comme clip que via QML, mais l’API du graphe de scène et le moteur de rendu par défaut peuvent utiliser n’importe quelle forme pour le découpage.
Lorsqu’on applique un découpage à une sous-arborescence, celle-ci doit être rendue avec un état OpenGL unique. Cela signifie que lorsque ` Item::clip ` est vrai, le regroupement (batching) de cet élément est limité à ses enfants. Lorsque ceux-ci sont nombreux, comme dans le cas d’un ` ListView ` ou d’un ` GridView`, ou complexes, comme dans le cas d’un ` TextArea`, cela ne pose pas de problème. Il convient toutefois d’utiliser la fonction « clip » avec prudence sur des éléments plus petits, car elle empêche le traitement par lots. Cela inclut les libellés de boutons, les champs de texte, les délégués de liste et les cellules de tableau. Le « clip » d’un Flickable (ou d’une vue d’élément) peut souvent être évité en organisant l’interface utilisateur de manière à ce que des éléments opaques recouvrent les zones autour du Flickable, et sinon en s’appuyant sur les bords de la fenêtre pour masquer tout le reste.
Le fait de définir ` Item::clip ` sur ` true ` active également le drapeau ` QQuickItem::ItemIsViewport ` ; les éléments enfants portant le drapeau ` QQuickItem::ItemObservesViewport ` peuvent alors utiliser la fenêtre d’affichage pour une étape de pré-recadrage approximative : par exemple, ` Text ` omet les lignes de texte qui se trouvent entièrement en dehors de la fenêtre d’affichage. L’omission de nœuds du graphe de scène ou la limitation de la ` vertices ` constitue une optimisation qui peut être réalisée en définissant ` flags ` en C++ plutôt qu’en définissant ` Item::clip ` en QML.
Lors de l’implémentation de la méthode ` QQuickItem::updatePaintNode()` dans un élément personnalisé, si celui-ci est capable d’afficher de nombreux détails sur une vaste zone géométrique, il convient de se demander s’il est efficace de limiter le rendu graphique à la fenêtre d’affichage ; si tel est le cas, vous pouvez activer le drapeau ` ItemObservesViewport ` et récupérer la zone actuellement visible via la méthode ` QQuickItem::clipRect()`. Cela a notamment pour conséquence que la fonction `updatePaintNode()` sera appelée plus souvent (généralement une fois par image dès lors que le contenu se déplace dans la fenêtre d'affichage).
Tampons de sommets
Chaque lot utilise un objet tampon de sommets (VBO) pour stocker ses données sur le GPU. Ce tampon de sommets est conservé d’une image à l’autre et mis à jour lorsque la partie du graphe de scène qu’il représente change.
Par défaut, le moteur de rendu charge les données dans le VBO à l’aide de ` GL_STATIC_DRAW`. Il est possible de sélectionner une autre stratégie de chargement en définissant la variable d’environnement ` QSG_RENDERER_BUFFER_STRATEGY=[strategy]`. Les valeurs valides sont ` stream ` et ` dynamic`. La modification de cette valeur est principalement utile pour les éditeurs de plateformes.
Anticrénelage
Le graphe de scène prend en charge deux types d’anticrénelage. Par défaut, les primitives telles que les rectangles et les images sont anticrénelées en ajoutant des sommets supplémentaires le long de leurs bords, de sorte que ceux-ci s’estompent progressivement jusqu’à devenir transparents. Nous appelons cette méthode « anticrénelage par sommets ». Si l’utilisateur demande un contexte OpenGL à échantillonnage multiple, en définissant une variable d’environnement QSurfaceFormat avec un nombre d’échantillons supérieur à 0 à l’aide de la fonction QQuickWindow::setFormat(), le graphe de scène privilégiera l’anticrénelage basé sur l’échantillonnage multiple (MSAA). Ces deux techniques influencent le fonctionnement interne du rendu et présentent des limitations différentes.
Il est également possible de redéfinir la méthode d’anticrénelage utilisée en définissant la variable d’environnement QSG_ANTIALIASING_METHOD sur vertex ou msaa.
L’anticrénelage par sommets peut produire des lignes de démarcation entre les arêtes de primitives adjacentes, même lorsque ces deux arêtes sont mathématiquement identiques. Ce n’est pas le cas de l’anticrénelage par échantillonnage multiple.
Anticrénelage des sommets
L’anticrénelage des sommets peut être activé ou désactivé élément par élément à l’aide de la propriété Item::antialiasing. Il fonctionne indépendamment des capacités du matériel sous-jacent et produit un anticrénelage de meilleure qualité, tant pour les primitives rendues normalement que pour celles capturées dans des objets framebuffer, par exemple à l’aide du type ShaderEffectSource.
L’inconvénient de l’utilisation de l’anticrénelage des sommets est que chaque primitive pour laquelle l’anticrénelage est activé devra faire l’objet d’un mélange. En termes de traitement par lots, cela signifie que le moteur de rendu doit fournir davantage d’efforts pour déterminer si la primitive peut être traitée en lot ou non ; de plus, en raison des chevauchements avec d’autres éléments de la scène, cela peut également entraîner une réduction du nombre de lots, ce qui pourrait avoir un impact sur les performances.
Sur du matériel bas de gamme, le mélange peut également s’avérer assez coûteux ; ainsi, pour une image ou un rectangle arrondi couvrant la majeure partie de l’écran, la quantité de mélange nécessaire à l’intérieur de ces primitives peut entraîner une perte de performances significative, car la primitive entière doit être mélangée.
Antialiasing multi-échantillonnage
L’anticrénelage multi-échantillon est une fonctionnalité matérielle dans laquelle le matériel calcule une valeur de couverture par pixel dans la primitive. Certains matériels peuvent effectuer un anticrénelage multi-échantillon à très faible coût, tandis que d’autres peuvent nécessiter à la fois davantage de mémoire et davantage de cycles GPU pour rendre une image.
Grâce à l’anticrénelage multi-échantillonnage, de nombreuses primitives, telles que les rectangles arrondis et les éléments d’image, peuvent être anticrénelées tout en restant opaques dans le graphe de scène. Cela signifie que le moteur de rendu a la tâche facilitée lors de la création de lots et peut s’appuyer sur la technique « early-z » pour éviter le surdessin.
Lorsque l’anticrénelage par échantillonnage multiple est utilisé, le contenu rendu dans des objets de tampon de trame nécessite des extensions supplémentaires pour prendre en charge l’échantillonnage multiple des tampons de trame. Il s’agit généralement de « GL_EXT_framebuffer_multisample » et « GL_EXT_framebuffer_blit ». La plupart des puces pour ordinateurs de bureau disposent de ces extensions, mais elles sont moins courantes sur les puces embarquées. Lorsque le multisampling des framebuffers n’est pas disponible au niveau matériel, le contenu rendu dans les objets framebuffer ne sera pas lissé, y compris le contenu d’un « ShaderEffectSource ».
Performances
Comme indiqué au début, il n’est pas nécessaire de comprendre les détails les plus subtils du moteur de rendu pour obtenir de bonnes performances. Il est conçu pour optimiser les cas d’utilisation courants et fonctionnera très bien dans presque toutes les circonstances.
- De bonnes performances découlent d’un regroupement efficace, avec le moins possible de géométrie chargée à plusieurs reprises. En définissant la variable d’environnement
QSG_RENDERER_DEBUG=render, le moteur de rendu affichera des statistiques sur l’efficacité du regroupement en lots, le nombre de lots utilisés, ainsi que les lots conservés et ceux qui sont opaques ou non. Pour obtenir des performances optimales, les chargements ne doivent avoir lieu qu’en cas de réelle nécessité ; le nombre de lots doit être inférieur à 10 et au moins 3 à 4 d’entre eux doivent être opaques. - Le moteur de rendu par défaut n’effectue ni découpage de la fenêtre d’affichage côté CPU, ni détection d’occlusion. Si un élément n’est pas censé être visible, il ne doit pas s’afficher. Utilisez `
Item::visible: false` pour les éléments qui ne doivent pas être dessinés. La principale raison pour laquelle cette logique n’a pas été intégrée est qu’elle entraîne un surcoût qui pénaliserait également les applications ayant pris soin de bien se comporter. - Assurez-vous que l’atlas de textures est utilisé. Les éléments `Image` et ` BorderImage ` l’utiliseront, sauf si l’image est trop volumineuse. Pour les textures créées en C++, transmettez ` QQuickWindow::TextureCanUseAtlas ` lors de l’appel à `QQuickWindow::createTexture()`. En définissant la variable d’environnement `
QSG_ATLAS_OVERLAY`, toutes les textures de l’atlas seront colorées afin d’être facilement identifiables dans l’application. - Utilisez des primitives opaques dans la mesure du possible. Les primitives opaques sont plus rapides à traiter par le moteur de rendu et plus rapides à dessiner sur le GPU. Par exemple, les fichiers PNG comportent souvent un canal alpha, même si chaque pixel est entièrement opaque. Les fichiers JPG sont toujours opaques. Lorsque vous fournissez des images à un « QQuickImageProvider » ou que vous créez des images avec ` QQuickWindow::createTextureFromImage()` (), veillez à ce que l’image ait la valeur ` QImage::Format_RGB32` , dans la mesure du possible.
- Sachez que les éléments composés qui se chevauchent, comme dans l’illustration ci-dessus, ne peuvent pas être traités par lots.
- Le rognage interrompt le traitement par lots. Ne l’utilisez jamais au niveau d’un élément individuel, à l’intérieur de cellules de tableau, de délégués d’éléments ou autres éléments similaires. Au lieu de rogner du texte, utilisez l’élision. Au lieu de rogner une image, créez une fonction « QQuickImageProvider » qui renvoie une image recadrée.
- Le traitement par lots ne fonctionne qu’avec des indices 16 bits. Tous les éléments intégrés utilisent des indices 16 bits, mais une géométrie personnalisée peut également utiliser des indices 32 bits.
- Certains indicateurs de matériau empêchent le traitement par lots, le plus restrictif étant « QSGMaterial::RequiresFullMatrix », qui empêche tout traitement par lots.
- Les applications avec un arrière-plan monochrome doivent le définir à l’aide de ` QQuickWindow::setColor()` plutôt que d’utiliser un élément `Rectangle` de niveau supérieur. ` QQuickWindow::setColor()` sera utilisé dans un appel à `
glClear()`, ce qui est potentiellement plus rapide. - Les éléments d’image mipmappés ne sont pas placés dans l’atlas global et ne feront pas l’objet d’un traitement par lots.
- Un bug dans le pilote OpenGL lié aux relectures d’objets de tampon de trame (FBO) peut corrompre les glyphes rendus. Si vous définissez la variable d’environnement `
QML_USE_GLYPHCACHE_WORKAROUND`, Qt conserve une copie supplémentaire du glyphe en mémoire vive. Cela signifie que les performances sont légèrement réduites lors du dessin de glyphes qui n’ont jamais été dessinés auparavant, car Qt accède à cette copie supplémentaire via le processeur. Cela signifie également que le cache de glyphes utilisera deux fois plus de mémoire. La qualité n’en est pas affectée.
Si une application présente des performances insuffisantes, assurez-vous que le rendu est bien le goulot d'étranglement. Utilisez un profileur ! La variable d’environnement QSG_RENDER_TIMING=1 fournira un certain nombre de paramètres de chronométrage utiles qui peuvent aider à identifier précisément l’origine du problème.
Groupes de mutabilité
Parfois, l’analyse révèle que le moteur de rendu regroupe du contenu régulièrement mis à jour avec du contenu statique. Dans ce cas, il vaut mieux diviser la géométrie en deux lots distincts plutôt que de charger un lot plus volumineux contenant principalement du contenu inchangé à chaque mise à jour.
Prenons l’exemple suivant :
Column {
Timer {
interval: 1000
running: true
repeat: true
onTriggered: dynamicText.counter++
}
Text {
id: dynamicText
property int counter: 0
text: counter
mutabilityGroup: Item.DynamicMutabilityGroup
}
Text {
id: staticText1
text: "Static label"
}
Text {
id: staticText2
text: "Static label"
}
}Cette application se compose d’une étiquette « dynamicText » qui se met à jour toutes les secondes. Elle comporte en outre deux étiquettes de texte statiques qui ne sont jamais mises à jour.
Si aucun groupe de mutabilité n’était explicitement défini dans cet exemple, la géométrie des étiquettes de texte statiques serait regroupée avec celle de l’étiquette dynamique. En effet, ils utilisent tous le même matériau et les mêmes propriétés de base, et le moteur de rendu par lots du graphe de scène tentera de minimiser le nombre d’appels de dessin nécessaires pour afficher tout le texte de la scène (dans une certaine limite). Par conséquent, les 88 sommets représentant le texte statique de la scène seront rechargés à chaque fois que le texte dynamique changera.
En définissant le paramètre `mutabilityGroup` de l’ dynamicText ` sur ` Item.DynamicMutabilityGroup`, nous indiquons au moteur de rendu que le texte sera fréquemment mis à jour. De ce fait, il ne sera jamais regroupé avec le reste du contenu, qui appartient au groupe de mutabilité par défaut. Si plusieurs éléments sont mis à jour au même rythme, ils peuvent tout de même être regroupés en les affectant au même groupe.
Au total, 16 groupes de mutabilité sont disponibles, y compris le groupe par défaut 0.
Pour la grande majorité des éléments de l’interface utilisateur, le groupe de mutabilité par défaut doit être utilisé. Il ne doit être remplacé que dans les cas où l’analyse révèle un goulot d’étranglement causé par le rechargement de géométries statiques volumineuses.
Visualisation
Pour visualiser les différents aspects du moteur de rendu par défaut du graphe de scène, la variable d’environnement QSG_VISUALIZE peut être définie sur l’une des valeurs détaillées dans chaque section ci-dessous. Nous fournissons des exemples de sortie de certaines de ces variables à l’aide du code QML suivant :
import QtQuick 2.2
Rectangle {
width: 200
height: 140
ListView {
id: clippedList
x: 20
y: 20
width: 70
height: 100
clip: true
model: ["Item A", "Item B", "Item C", "Item D"]
delegate: Rectangle {
color: "lightblue"
width: parent.width
height: 25
Text {
text: modelData
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
ListView {
id: clippedDelegateList
x: clippedList.x + clippedList.width + 20
y: 20
width: 70
height: 100
clip: true
model: ["Item A", "Item B", "Item C", "Item D"]
delegate: Rectangle {
color: "lightblue"
width: parent.width
height: 25
clip: true
Text {
text: modelData
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
}Pour l’ ListView à gauche, nous avons défini sa propriété ` clip ` sur ` true`. Pour l’ ListView à droite, nous avons également défini la propriété ` clip ` de chaque délégué sur ` true ` afin d’illustrer les effets du découpage sur le traitement par lots.

Original
Remarque : les éléments visualisés ne respectent pas le clipping, et l'ordre de rendu est arbitraire.
Visualisation des lots
En définissant ` QSG_VISUALIZE ` sur ` batches `, les lots sont visualisés dans le moteur de rendu. Les lots fusionnés sont représentés par une couleur unie, tandis que les lots non fusionnés sont représentés par un motif de lignes diagonales. Un nombre réduit de couleurs distinctes indique un bon regroupement en lots. Les lots non fusionnés sont à éviter s’ils contiennent de nombreux nœuds individuels.

QSG_VISUALIZE=batches
Visualisation du découpage
En définissant l’option « QSG_VISUALIZE » sur « clip », des zones rouges s’affichent par-dessus la scène pour indiquer le clipping. Comme les éléments « Qt Quick » ne sont pas clippés par défaut, aucun clipping n’est généralement visualisé.

QSG_VISUALIZE=clip
Visualisation des modifications
En définissant « QSG_VISUALIZE » sur « changes », les modifications sont visualisées dans le moteur de rendu. Les modifications apportées au graphe de scène sont visualisées par une superposition clignotante de couleur aléatoire. Les modifications apportées à une primitive sont visualisées par une couleur unie, tandis que les modifications apportées à un ancêtre, telles que les changements de matrice ou d’opacité, sont visualisées par un motif.
Visualisation du surdessin
En définissant QSG_VISUALIZE sur overdraw, le rendu affiche les chevauchements. Visualisez tous les éléments en 3D pour mettre en évidence les chevauchements. Ce mode peut également être utilisé pour détecter, dans une certaine mesure, la géométrie située en dehors de la fenêtre d'affichage. Les éléments opaques sont rendus avec une teinte verte, tandis que les éléments translucides sont rendus avec une teinte rouge. Le rectangle englobant de la fenêtre d’affichage est affiché en bleu. Le contenu opaque est plus facile à traiter pour le graphe de scène et son rendu est généralement plus rapide.
Notez que le rectangle racine dans le code ci-dessus est superflu, car la fenêtre est également blanche ; dans ce cas, dessiner ce rectangle constitue donc un gaspillage de ressources. Le remplacer par un `Item` peut apporter un léger gain de performances.


QSG_VISUALIZE=overdraw
Rendu via l’interface matérielle de rendu Qt
À partir de Qt 6.0, l’adaptation par défaut effectue toujours le rendu via une couche d’abstraction graphique, l’interface matérielle de rendu Qt (RHI), fournie par le Qt GUI module. Cela signifie que, contrairement à Qt 5, aucun appel OpenGL direct n’est effectué par le graphe de scène. Au lieu de cela, celui-ci enregistre les commandes de ressources et de dessin à l’aide des API RHI, qui traduisent ensuite le flux de commandes en appels OpenGL, Vulkan, Metal ou Direct 3D. La gestion des shaders est également unifiée : il suffit d’écrire le code du shader une seule fois, de le compiler en SPIR-V, puis de le traduire dans le langage adapté aux différentes API graphiques.
Pour contrôler ce comportement, les variables d’environnement suivantes peuvent être utilisées :
| Variable d’environnement | Valeurs possibles | Description |
|---|---|---|
QSG_RHI_BACKEND | vulkan, metal, opengl, d3d11, d3d12 | Demande l’utilisation du backend RHI spécifique. Par défaut, l’API graphique ciblée est choisie en fonction de la plateforme, sauf si elle est remplacée par cette variable ou par les API C++ équivalentes. Les valeurs par défaut sont actuellement Direct3D 11 pour Windows, Metal pour macOS et OpenGL pour les autres plateformes. |
QSG_INFO | 1 | Comme pour le chemin de rendu basé sur OpenGL, la définition de cette variable permet d’afficher les informations système lors de l’initialisation du graphe de scène d’ Qt Quick. Cela peut s’avérer très utile pour le dépannage. |
QSG_RHI_DEBUG_LAYER | 1 | Le cas échéant (Vulkan, Direct3D), active les couches de débogage ou de validation de l’implémentation de l’API graphique, si elles sont disponibles, soit sur le périphérique graphique, soit sur l’objet d’instance. Pour Metal sur macOS, définissez plutôt la variable d’environnement METAL_DEVICE_WRAPPER_TYPE=1. |
QSG_RHI_PREFER_SOFTWARE_RENDERER | 1 | Demande le choix d’un adaptateur ou d’un périphérique physique utilisant une rastérisation logicielle. Applicable uniquement lorsque l’API sous-jacente prend en charge l’énumération des adaptateurs (par exemple, Direct3D ou Vulkan) ; dans le cas contraire, cette option est ignorée. |
Les applications souhaitant toujours s’exécuter avec une seule API graphique donnée peuvent également en faire la demande via C++. Par exemple, l’appel suivant, effectué au début de la fonction `main()`, avant la construction de tout ` QQuickWindow`, force l’utilisation de Vulkan (et échouera dans le cas contraire) :
QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);Voir QSGRendererInterface::GraphicsApi. Les valeurs de l’énumération OpenGL, Vulkan, Metal, Direct3D11, Direct3D12 ont pour effet équivalent à celui d’une exécution avec QSG_RHI_BACKEND défini sur la clé de chaîne correspondante.
Tous les backends QRhi choisiront l’adaptateur GPU ou le périphérique physique par défaut du système, sauf si ce choix est remplacé par QSG_RHI_PREFER_SOFTWARE_RENDERER ou par une variable spécifique au backend, telle que QT_D3D_ADAPTER_INDEX ou QT_VK_PHYSICAL_DEVICE_INDEX. Aucune autre possibilité de configuration de l’adaptateur n’est proposée pour le moment.
À partir de Qt 6.5, certains paramètres qui n’étaient auparavant accessibles que sous forme de variables d’environnement sont désormais disponibles sous forme d’API C++ dans QQuickGraphicsConfiguration. Par exemple, définir ` QSG_RHI_DEBUG_LAYER ` et appeler ` setDebugLayer` (true) reviennent au même.
© 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.