Qt Quick Standard-Renderer für den Szenenbaum
Dieses Dokument erläutert die interne Funktionsweise des Standard-Renderers des Szenengraphen, damit man Code schreiben kann, der ihn sowohl hinsichtlich der Leistung als auch der Funktionen optimal nutzt.
Man muss die Interna des Renderers nicht verstehen, um eine gute Leistung zu erzielen. Es kann jedoch hilfreich sein, wenn man ihn in den Szenenbaum integriert oder herausfinden möchte, warum es nicht möglich ist, die maximale Effizienz aus dem Grafikchip herauszuholen.
Hinweis: Selbst wenn jeder Frame einzigartig ist und alles von Grund auf neu geladen wird, liefert der Standard-Renderer eine gute Leistung.
Die „ Qt Quick “-Elemente in einer QML-Szene füllen einen Baum aus „ QSGNode “-Instanzen. Nach seiner Erstellung stellt dieser Baum eine vollständige Beschreibung dar, wie ein bestimmtes Bild gerendert werden soll. Er enthält keinerlei Verweise zurück auf die „ Qt Quick “-Elemente und wird auf den meisten Plattformen in einem separaten Thread verarbeitet und gerendert. Der Renderer ist ein in sich geschlossener Teil des Szenengraphen, der den Baum „ QSGNode “ durchläuft und die in „ QSGGeometryNode “ definierte Geometrie sowie den in „ QSGMaterial “ definierten Shader-Zustand verwendet, um den Grafikzustand zu aktualisieren und Zeichenaufrufe zu generieren.
Bei Bedarf kann der Renderer mithilfe der internen Backend-API des Szenengraphen vollständig ersetzt werden. Dies ist vor allem für Plattformanbieter interessant, die die Vorteile nicht standardmäßiger Hardwarefunktionen nutzen möchten. Für die meisten Anwendungsfälle ist der Standard-Renderer ausreichend.
Der Standard-Renderer konzentriert sich auf zwei Hauptstrategien zur Optimierung des Renderings: das Batching von Draw-Calls und das Beibehalten von Geometrie auf der GPU.
Batching
Während eine herkömmliche 2D-API wie QPainter, Cairo oder Context2D darauf ausgelegt ist, Tausende einzelner Draw-Calls pro Frame zu verarbeiten, erzielen OpenGL und andere hardwarebeschleunigte APIs die beste Leistung, wenn die Anzahl der Draw-Calls sehr gering ist und Statusänderungen auf ein Minimum beschränkt werden.
Hinweis: Während in den folgenden Abschnitten „ OpenGL “ als Beispiel herangezogen wird, gelten die gleichen Konzepte auch für andere Grafik-APIs.
Betrachten wir den folgenden Anwendungsfall:

glDrawArrays Die einfachste Art, diese Liste zu zeichnen, ist Zelle für Zelle. Zunächst wird der Hintergrund gezeichnet. Dabei handelt es sich um ein Rechteck in einer bestimmten Farbe. In OpenGL-Begriffen bedeutet dies, ein Shader-Programm für die Füllung mit einer einheitlichen Farbe auszuwählen, die Füllfarbe festzulegen, die Transformationsmatrix mit den x- und y-Versätzen einzurichten und dann beispielsweise mit ` glDrawArrays ` zwei Dreiecke zu zeichnen, aus denen das Rechteck besteht. Als Nächstes wird das Symbol gezeichnet. In OpenGL-Begriffen bedeutet dies, ein Shader-Programm zum Zeichnen von Texturen auszuwählen, die zu verwendende aktive Textur auszuwählen, die Transformationsmatrix festzulegen, Alpha-Blending zu aktivieren und dann beispielsweise mit ` ` die beiden Dreiecke zu zeichnen, aus denen das umschließende Rechteck des Symbols besteht. Der Text und die Trennlinie zwischen den Zellen folgen einem ähnlichen Muster. Und dieser Vorgang wird für jede Zelle in der Liste wiederholt, sodass bei einer längeren Liste der durch OpenGL-Zustandsänderungen und Zeichenaufrufe verursachte Overhead den Vorteil, den die Verwendung einer hardwarebeschleunigten API bieten könnte, bei weitem überwiegt.
Wenn die einzelnen Primitive groß sind, ist dieser Mehraufwand vernachlässigbar, doch bei einer typischen Benutzeroberfläche gibt es viele kleine Elemente, die zusammen einen beträchtlichen Mehraufwand ergeben.
Der Standard-Scene-Graph-Renderer arbeitet innerhalb dieser Einschränkungen und versucht, einzelne Primitive zu Batches zusammenzufassen, wobei das visuelle Ergebnis exakt gleich bleibt. Das Ergebnis sind weniger OpenGL-Zustandsänderungen und eine minimale Anzahl von Zeichenaufrufen, was zu einer optimalen Leistung führt.
Opake Primitive
Der Renderer unterscheidet zwischen undurchsichtigen Primitiven und solchen, die Alpha-Blending erfordern. Durch die Verwendung des OpenGL-Z-Puffers und die Zuweisung einer eindeutigen Z-Position an jede Primitive kann der Renderer undurchsichtige Primitive frei neu anordnen, ohne Rücksicht auf ihre Position auf dem Bildschirm oder darauf, mit welchen anderen Elementen sie sich überlappen. Anhand des Materialstatus jeder Primitive erstellt der Renderer opake Batches. Zu den Kernelementen von „ Qt Quick “ gehören dabei Rechteckelemente mit opaken Farben sowie vollständig opake Bilder wie JPEGs oder BMPs.
Ein weiterer Vorteil der Verwendung undurchsichtiger Primitive besteht darin, dass für diese keine Aktivierung von „ GL_BLEND “ erforderlich ist, was insbesondere auf mobilen und eingebetteten GPUs sehr rechenintensiv sein kann.
Opake Primitive werden bei aktivierter „ glDepthMask “- und „ GL_DEPTH_TEST “-Funktion von vorne nach hinten gerendert. Auf GPUs, die intern „Early-Z“-Prüfungen durchführen, bedeutet dies, dass der Fragment-Shader für verdeckte Pixel oder Pixelblöcke nicht ausgeführt werden muss. Beachten Sie, dass der Renderer diese Knoten dennoch berücksichtigen muss und der Vertex-Shader weiterhin für jeden Scheitelpunkt in diesen Primitiven ausgeführt wird. Wenn die Anwendung also weiß, dass etwas vollständig verdeckt ist, ist es am besten, es explizit mithilfe von „ Item::visible “ oder „ Item::opacity “ auszublenden.
Hinweis: Der „ Item::z “ dient zur Steuerung der Stapelreihenfolge eines Elements im Verhältnis zu seinen Geschwistern. Er steht in keinem direkten Zusammenhang mit dem Renderer und dem Z-Buffer von OpenGL.
Alpha-gemischte Primitive
Sobald undurchsichtige Primitive gezeichnet wurden, deaktiviert der Renderer die „ glDepthMask “, aktiviert „ GL_BLEND “ und rendert alle alpha-gemischten Primitive in der Reihenfolge „von hinten nach vorne“.
Das Batching von Alpha-Blended-Primitiven erfordert etwas mehr Aufwand im Renderer, da sich überlappende Elemente in der richtigen Reihenfolge gerendert werden müssen, damit das Alpha-Blending korrekt aussieht. Sich allein auf den Z-Buffer zu verlassen, reicht nicht aus. Der Renderer durchläuft alle mit Alpha-Blending versehenen Primitive und prüft neben ihrem Materialstatus auch deren Begrenzungsrechteck, um festzustellen, welche Elemente gebündelt werden können und welche nicht.

Im Fall ganz links können die blauen Hintergründe in einem Aufruf und die beiden Textelemente in einem anderen Aufruf gezeichnet werden, da sich die Texte nur mit einem Hintergrund überlappen, vor dem sie angeordnet sind. Im Fall ganz rechts überlappt der Hintergrund von „Item 4“ den Text von „Item 3“, sodass in diesem Fall sowohl die Hintergründe als auch die Texte in separaten Aufrufen gezeichnet werden müssen.
In Z-Richtung sind die Alpha-Primitive mit den undurchsichtigen Knoten verschachtelt und können, sofern verfügbar, „Early-Z“ auslösen; allerdings ist es auch hier immer schneller, „ Item::visible “ auf „false“ zu setzen.
Kombination mit 3D-Primitiven
Der Szenengraph unterstützt sowohl Pseudo-3D- als auch echte 3D-Primitive. So lässt sich beispielsweise ein „Page-Curl“-Effekt mithilfe von „ ShaderEffect “ implementieren oder ein Bumpmapped-Torus mithilfe von „ QSGGeometry “ und einem benutzerdefinierten Material. Dabei ist zu beachten, dass der Standard-Renderer bereits den Tiefenpuffer nutzt.
Der Renderer modifiziert den von QSGMaterialShader::vertexShader() zurückgegebenen Vertex-Shader und komprimiert die z-Werte der Vertex-Daten, nachdem die Modell-Ansichts- und Projektionsmatrizen angewendet wurden; anschließend fügt er eine kleine Verschiebung auf der z-Achse hinzu, um sie an die richtige z-Position zu bringen.
Die Komprimierung setzt voraus, dass die z-Werte im Bereich von 0 bis 1 liegen.
Texturatlas
Die aktive Textur ist ein eindeutiger OpenGL-Zustand, was bedeutet, dass mehrere Primitive, die unterschiedliche OpenGL-Texturen verwenden, nicht gebündelt werden können. Der Qt Quick -Szenenbaum ermöglicht aus diesem Grund die Zuweisung mehrerer „ QSGTexture “-Instanzen als kleinere Teilbereiche einer größeren Textur – eines Texturatlas.
Der größte Vorteil von Texturatlasen besteht darin, dass mehrere „ QSGTexture “-Instanzen nun auf dieselbe OpenGL-Texturinstanz verweisen. Dadurch ist es möglich, auch texturierte Zeichenaufrufe zu bündeln, wie beispielsweise „Image“-Elemente, „ BorderImage “-Elemente, „ ShaderEffect “-Elemente sowie C++-Typen wie „ QSGSimpleTextureNode “ und benutzerdefinierte „QSGGeometryNodes“, die Texturen verwenden.
Hinweis: Große Texturen werden nicht in den Texturatlas aufgenommen.
Atlasbasierte Texturen werden erstellt, indem „ QQuickWindow::TextureCanUseAtlas “ an die Funktion „ QQuickWindow::createTextureFromImage()“ übergeben wird.
Hinweis: Atlasbasierte Texturen haben keine Texturkoordinaten im Bereich von 0 bis 1. Verwenden Sie ` QSGTexture::normalizedTextureSubRect()` (), um die Atlas-Texturkoordinaten abzurufen.
Der Szenengraph verwendet Heuristiken, um zu ermitteln, wie groß der Atlas sein sollte und ab welcher Größe Texturen in den Atlas aufgenommen werden. Falls andere Werte erforderlich sind, können diese über die Umgebungsvariablen ` QSG_ATLAS_WIDTH=[width]`, ` QSG_ATLAS_HEIGHT=[height] ` und ` QSG_ATLAS_SIZE_LIMIT=[size]` überschrieben werden. Die Änderung dieser Werte ist vor allem für Plattformanbieter von Interesse.
Batch-Roots
Neben der Zusammenführung kompatibler Primitive zu Batches versucht der Standard-Renderer auch, die Datenmenge zu minimieren, die für jedes Bild an die GPU gesendet werden muss. Der Standard-Renderer identifiziert zusammengehörige Teilbäume und versucht, diese in separate Batches zu gruppieren. Sobald Batches identifiziert sind, werden sie zusammengeführt, hochgeladen und mithilfe von Vertex-Buffer-Objekten im GPU-Speicher abgelegt.
Transformationsknoten
Jedes „ Qt Quick “-Element fügt einen „ QSGTransformNode “ in den Szenengraphbaum ein, um dessen x-, y-, Skalierungs- oder Rotationswerte zu verwalten. Unter diesem Transformationsknoten werden untergeordnete Elemente angelegt. Der Standard-Renderer verfolgt den Zustand von Transformationsknoten zwischen den Frames und prüft Teilbäume, um zu entscheiden, ob ein Transformationsknoten ein geeigneter Kandidat ist, um als Wurzel für eine Reihe von Batches zu dienen. Ein Transformationsknoten, der sich zwischen den Frames ändert und einen relativ komplexen Teilbaum aufweist, kann zur Batch-Wurzel werden.
QSGGeometryNodes im Teilbaum eines Batch-Stammknotens werden relativ zum Stammknoten auf der CPU vorverarbeitet. Anschließend werden sie auf die GPU hochgeladen und dort beibehalten. Wenn sich die Transformation ändert, muss der Renderer nur die Matrix des Stammknotens aktualisieren, nicht jedes einzelne Element, was das Scrollen in Listen und Rastern sehr schnell macht. Bei aufeinanderfolgenden Frames ist das Rendern der Liste praktisch kostenlos, solange keine Knoten hinzugefügt oder entfernt werden. Wenn neuer Inhalt in den Teilbaum gelangt, wird der Batch, der diesen enthält, neu aufgebaut, was jedoch immer noch relativ schnell erfolgt. Beim Scrollen durch ein Raster oder eine Liste gibt es in der Regel mehrere unveränderte Frames pro Frame, in dem Knoten hinzugefügt oder entfernt wurden.
Ein weiterer Vorteil der Identifizierung von Transformationsknoten als Batch-Wurzeln besteht darin, dass der Renderer dadurch die Teile des Baums beibehalten kann, die sich nicht verändert haben. Nehmen wir zum Beispiel an, eine Benutzeroberfläche besteht aus einer Liste und einer Schaltflächenzeile. Wenn durch die Liste gescrollt wird und Delegaten hinzugefügt oder entfernt werden, bleibt der Rest der Benutzeroberfläche – die Schaltflächenzeile – unverändert und kann mithilfe der bereits auf der GPU gespeicherten Geometrie gezeichnet werden.
Der Schwellenwert für Knoten und Eckpunkte, ab dem ein Transformationsknoten zum Batch-Root wird, kann mithilfe der Umgebungsvariablen „ QSG_RENDERER_BATCH_NODE_THRESHOLD=[count] “ und „ QSG_RENDERER_BATCH_VERTEX_THRESHOLD=[count] “ überschrieben werden. Das Überschreiben dieser Flags ist vor allem für Plattformanbieter von Nutzen.
Hinweis: Unterhalb einer Batch-Root wird für jede eindeutige Kombination aus Materialzustand und Geometrietyp ein Batch erstellt.
Clipping
Wenn „ Item::clip “ auf „true“ gesetzt wird, wird ein „ QSGClipNode “ mit einem Rechteck in seiner Geometrie erstellt. Der Standard-Renderer wendet diesen Clipping-Bereich mithilfe von „Scissoring“ in OpenGL an. Wenn das Element um einen Winkel gedreht wird, der nicht 90 Grad beträgt, wird der Stencil-Puffer von OpenGL verwendet. „ Qt Quick “ „Item“ unterstützt über QML lediglich die Festlegung eines Rechtecks als Clip, doch die Szenengraph-API und der Standard-Renderer können beliebige Formen für das Clipping verwenden.
Wenn ein Clip auf einen Teilbaum angewendet wird, muss dieser Teilbaum mit einem eindeutigen OpenGL-Zustand gerendert werden. Das bedeutet, dass bei „ Item::clip “ = true das Batching dieses Elements auf seine untergeordneten Elemente beschränkt ist. Bei vielen untergeordneten Elementen, wie z. B. einem „ ListView “ oder „ GridView “, oder bei komplexen untergeordneten Elementen, wie z. B. einem „ TextArea “, ist dies unproblematisch. Bei kleineren Elementen sollte man „clip“ jedoch mit Vorsicht einsetzen, da es die Batch-Verarbeitung verhindert. Dazu gehören Schaltflächenbeschriftungen, Textfelder oder Listen-Delegates sowie Tabellenzellen. Das Beschneiden eines „Flickable“ (oder einer Elementansicht) lässt sich oft vermeiden, indem man die Benutzeroberfläche so anordnet, dass undurchsichtige Elemente Bereiche um das „Flickable“ herum abdecken, und ansonsten darauf vertraut, dass die Fensterränder alles andere abschneiden.
Das Setzen von ` Item::clip ` auf ` true ` setzt auch das Flag ` QQuickItem::ItemIsViewport `; untergeordnete Elemente mit dem Flag ` QQuickItem::ItemObservesViewport ` können den Viewport für einen groben Vor-Clipping-Schritt nutzen: Beispielsweise lässt ` Text ` Textzeilen weg, die vollständig außerhalb des Viewports liegen. Das Weglassen von Scene-Graph-Knoten oder die Begrenzung des ` vertices ` ist eine Optimierung, die durch das Setzen von ` flags ` in C++ anstelle von ` Item::clip ` in QML erreicht werden kann.
Wenn Sie ` QQuickItem::updatePaintNode()` in einem benutzerdefinierten Element implementieren und dieses viele Details über einen großen geometrischen Bereich rendern kann, sollten Sie überlegen, ob es effizient ist, die Grafik auf den Ansichtsbereich zu beschränken; wenn ja, können Sie das Flag ` ItemObservesViewport ` setzen und den aktuell sichtbaren Bereich über ` QQuickItem::clipRect()` abfragen. Eine Folge davon ist, dass `updatePaintNode()` häufiger aufgerufen wird (in der Regel einmal pro Frame, sobald sich Inhalte im Ansichtsbereich bewegen).
Vertex-Puffer
Jeder Batch verwendet ein Vertex-Buffer-Objekt (VBO), um seine Daten auf der GPU zu speichern. Dieser Vertex-Buffer bleibt zwischen den Frames erhalten und wird aktualisiert, wenn sich der Teil des Szenengraphen, den er repräsentiert, ändert.
Standardmäßig lädt der Renderer Daten mithilfe von ` GL_STATIC_DRAW` in das VBO hoch. Es ist möglich, eine andere Upload-Strategie auszuwählen, indem die Umgebungsvariable ` QSG_RENDERER_BUFFER_STRATEGY=[strategy]` gesetzt wird. Gültige Werte sind ` stream ` und ` dynamic`. Die Änderung dieses Werts ist vor allem für Plattformanbieter nützlich.
Antialiasing
Der Szenengraph unterstützt zwei Arten von Antialiasing. Standardmäßig werden Primitive wie Rechtecke und Bilder durch Hinzufügen weiterer Scheitelpunkte entlang der Kanten der Primitive geglättet, sodass die Kanten transparent auslaufen. Wir bezeichnen diese Methode als „Vertex-Antialiasing“. Wenn der Benutzer einen OpenGL-Kontext mit Multisampling anfordert, indem er die Umgebungsvariable „ QSurfaceFormat “ mit einer Anzahl von Samples größer als „ 0 “ mithilfe von „ QQuickWindow::setFormat()“ setzt, bevorzugt der Szenengraph das auf Multisampling basierende Antialiasing (MSAA). Die beiden Techniken beeinflussen die interne Funktionsweise des Renderings und weisen unterschiedliche Einschränkungen auf.
Es ist außerdem möglich, die verwendete Antialiasing-Methode zu überschreiben, indem die Umgebungsvariable ` QSG_ANTIALIASING_METHOD ` entweder auf ` vertex ` oder ` msaa` gesetzt wird.
Vertex-Antialiasing kann zu Übergängen zwischen den Kanten benachbarter Primitive führen, selbst wenn die beiden Kanten mathematisch identisch sind. Multisample-Antialiasing tut dies nicht.
Vertex-Antialiasing
Vertex-Antialiasing kann mithilfe der Eigenschaft „ Item::antialiasing “ für jedes Element einzeln aktiviert und deaktiviert werden. Es funktioniert unabhängig davon, was die zugrunde liegende Hardware unterstützt, und erzeugt eine höhere Antialiasing-Qualität – sowohl für normal gerenderte Primitive als auch für Primitive, die in Framebuffer-Objekte erfasst werden, beispielsweise mithilfe des Typs „ ShaderEffectSource “.
Der Nachteil der Verwendung von Vertex-Antialiasing besteht darin, dass jede Primitive, für die Antialiasing aktiviert ist, gemischt werden muss. Im Hinblick auf das Batching bedeutet dies, dass der Renderer mehr Arbeit aufwenden muss, um festzustellen, ob die Primitive gebatcht werden kann oder nicht; aufgrund von Überlappungen mit anderen Elementen in der Szene kann dies zudem zu weniger Batching führen, was sich auf die Leistung auswirken könnte.
Auf Low-End-Hardware kann das Blenden zudem recht rechenintensiv sein; bei einem Bild oder einem abgerundeten Rechteck, das den größten Teil des Bildschirms bedeckt, kann der für das Innere dieser Primitive erforderliche Blending-Aufwand zu erheblichen Leistungseinbußen führen, da die gesamte Primitive geblendet werden muss.
Multisample-Antialiasing
Multisample-Antialiasing ist eine Hardwarefunktion, bei der die Hardware einen Abdeckungswert pro Pixel in der Primitive berechnet. Manche Hardware kann Multisample mit sehr geringem Aufwand durchführen, während andere Hardware möglicherweise sowohl mehr Speicher als auch mehr GPU-Zyklen benötigt, um einen Frame zu rendern.
Durch die Verwendung von Multisample-Antialiasing können viele Primitive, wie beispielsweise abgerundete Rechtecke und Bildelemente, geglättet werden und bleiben dennoch im Szenengraphen undurchsichtig. Das bedeutet, dass der Renderer bei der Erstellung von Batches weniger Arbeit hat und sich auf „Early-Z“ verlassen kann, um Überzeichnen zu vermeiden.
Bei Verwendung von Multisample-Antialiasing benötigen Inhalte, die in Framebuffer-Objekte gerendert werden, zusätzliche Erweiterungen, um das Multisampling von Framebuffern zu unterstützen. Typischerweise sind dies „ GL_EXT_framebuffer_multisample “ und „ GL_EXT_framebuffer_blit “. Die meisten Desktop-Chips verfügen über diese Erweiterungen, bei Embedded-Chips sind sie jedoch weniger verbreitet. Wenn Framebuffer-Multisampling in der Hardware nicht verfügbar ist, werden in Framebuffer-Objekte gerenderte Inhalte nicht geglättet, einschließlich des Inhalts eines „ ShaderEffectSource “.
Leistung
Wie eingangs erwähnt, ist es für eine gute Leistung nicht erforderlich, die Feinheiten des Renderers zu verstehen. Er ist auf gängige Anwendungsfälle optimiert und liefert unter fast allen Umständen eine recht gute Leistung.
- Eine gute Leistung ergibt sich aus effektivem Batching, bei dem so wenig Geometrie wie möglich immer wieder neu hochgeladen wird. Durch Setzen der Umgebungsvariable `
QSG_RENDERER_DEBUG=render` gibt der Renderer Statistiken darüber aus, wie gut das Batching funktioniert, wie viele Batches verwendet werden, welche Batches beibehalten werden und welche opak sind und welche nicht. Wenn man eine optimale Leistung anstrebt, sollten Uploads nur dann erfolgen, wenn sie wirklich notwendig sind; die Anzahl der Batches sollte unter 10 liegen, und mindestens 3–4 davon sollten opak sein. - Der Standard-Renderer führt weder CPU-seitiges Viewport-Clipping noch Okklusionserkennung durch. Wenn etwas nicht sichtbar sein soll, sollte es auch nicht angezeigt werden. Verwenden Sie `
Item::visible: false` für Elemente, die nicht gezeichnet werden sollen. Der Hauptgrund dafür, dass eine solche Logik nicht integriert wurde, ist, dass sie zusätzlichen Aufwand verursacht, der auch Anwendungen beeinträchtigen würde, die auf ein korrektes Verhalten achten. - Stellen Sie sicher, dass der Texturatlas verwendet wird. Die Elemente „Image“ und „ BorderImage “ nutzen ihn, sofern das Bild nicht zu groß ist. Bei in C++ erstellten Texturen übergeben Sie beim Aufruf von `QQuickWindow::createTexture()` die Variable ` QQuickWindow::TextureCanUseAtlas `. Durch Setzen der Umgebungsvariable `
QSG_ATLAS_OVERLAY` werden alle Atlas-Texturen eingefärbt, sodass sie in der Anwendung leicht zu erkennen sind. - Verwenden Sie nach Möglichkeit opake Primitive. Opake Primitive lassen sich im Renderer schneller verarbeiten und auf der GPU schneller zeichnen. Beispielsweise verfügen PNG-Dateien oft über einen Alphakanal, obwohl jedes Pixel vollständig undurchsichtig ist. JPG-Dateien sind immer undurchsichtig. Wenn Sie Bilder an einen „ QQuickImageProvider “ übergeben oder Bilder mit „ QQuickWindow::createTextureFromImage()“ erstellen, sollten Sie dem Bild nach Möglichkeit den Wert „ QImage::Format_RGB32 “ zuweisen.
- Beachten Sie, dass sich überlappende zusammengesetzte Elemente, wie in der obigen Abbildung dargestellt, nicht gebündelt werden können.
- Das Beschneiden unterbricht die Stapelverarbeitung. Verwenden Sie diese Funktion niemals auf Einzelobjektbasis, innerhalb von Tabellenzellen, Objektdelegaten oder Ähnlichem. Verwenden Sie anstelle des Beschneidens von Text die Auslassung. Anstelle des Beschneidens eines Bildes erstellen Sie einen „ QQuickImageProvider “, der ein zugeschnittenes Bild zurückgibt.
- Die Stapelverarbeitung funktioniert nur bei 16-Bit-Indizes. Alle integrierten Elemente verwenden 16-Bit-Indizes, aber eine benutzerdefinierte Geometrie kann auch 32-Bit-Indizes verwenden.
- Einige Material-Flags verhindern das Batching; das einschränkendste davon ist „ QSGMaterial::RequiresFullMatrix “, das jegliches Batching verhindert.
- Anwendungen mit einem monochromen Hintergrund sollten dies über „ QQuickWindow::setColor()“ festlegen, anstatt ein „Rectangle“-Element auf oberster Ebene zu verwenden. „ QQuickWindow::setColor()“ wird bei einem Aufruf von „
glClear()“ verwendet, was potenziell schneller ist. - Mipmapped-Image-Elemente werden nicht im globalen Atlas abgelegt und daher nicht gebündelt.
- Ein Fehler im OpenGL-Treiber im Zusammenhang mit dem Zurücklesen von Framebuffer-Objekten (FBO) kann zu fehlerhaften Glyphen führen. Wenn Sie die Umgebungsvariable `
QML_USE_GLYPHCACHE_WORKAROUND` setzen, behält Qt eine zusätzliche Kopie des Glyphs im Arbeitsspeicher. Dies bedeutet, dass die Leistung beim Zeichnen von Glyphen, die zuvor noch nicht gezeichnet wurden, geringfügig geringer ist, da Qt über die CPU auf die zusätzliche Kopie zugreift. Es bedeutet auch, dass der Glyphen-Cache doppelt so viel Speicher belegt. Die Qualität wird dadurch nicht beeinträchtigt.
Wenn eine Anwendung schlecht läuft, stellen Sie sicher, dass das Rendern tatsächlich der Engpass ist. Verwenden Sie einen Profiler! Die Umgebungsvariable ` QSG_RENDER_TIMING=1 ` gibt eine Reihe nützlicher Zeitparameter aus, die dabei helfen können, den Problemherd genau zu lokalisieren.
Mutabilitätsgruppen
Manchmal zeigt eine Analyse, dass der Renderer regelmäßig aktualisierte Inhalte zusammen mit statischen Inhalten bündelt. In solchen Fällen ist es besser, die Geometrie in zwei separate Batches aufzuteilen, anstatt bei jeder Aktualisierung einen größeren Batch mit größtenteils unveränderten Inhalten hochzuladen.
Betrachten Sie das folgende Beispiel:
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"
}
}Diese Anwendung besteht aus einem „ dynamicText “-Label, das sich jede Sekunde aktualisiert. Darüber hinaus verfügt sie über zwei statische Text-Labels, die niemals aktualisiert werden.
Wäre in diesem Beispiel keine explizite Mutabilitätsgruppe festgelegt, würde die Geometrie der statischen Textbeschriftungen zusammen mit der dynamischen Beschriftung gebündelt werden. Dies liegt daran, dass sie alle dasselbe Material und dieselben Grundeigenschaften verwenden und der Scene-Graph-Batch-Renderer versucht, die Anzahl der Draw-Calls zu minimieren, die zur Darstellung des gesamten Texts in der Szene erforderlich sind (bis zu einer bestimmten Grenze). Infolgedessen werden die 88 Scheitelpunkte, die den statischen Text in der Szene darstellen, jedes Mal neu hochgeladen, wenn sich der dynamische Text ändert.
Indem wir die „mutabilityGroup“ des „ dynamicText “ auf „ Item.DynamicMutabilityGroup “ setzen, geben wir dem Renderer einen Hinweis darauf, dass die Beschriftung häufig aktualisiert wird. Dadurch wird es niemals zusammen mit den übrigen Inhalten gebündelt, die zur Standard-Mutability-Gruppe gehören. Wenn mehrere Elemente im gleichen Tempo aktualisiert werden, können sie dennoch gebündelt werden, indem man sie derselben Gruppe zuordnet.
Insgesamt stehen 16 Mutabilitätsgruppen zur Verfügung, einschließlich der Standardgruppe „ 0 “.
Für die große Mehrheit der Elemente in der Benutzeroberfläche sollte die Standard-Mutabilitätsgruppe verwendet werden. Sie sollte in erster Linie nur dann überschrieben werden, wenn bei der Analyse ein Engpass festgestellt wird, der durch das erneute Laden großer, statischer Geometrien verursacht wird.
Visualisierung
Um die verschiedenen Aspekte des Standard-Renderers des Szenengraphen zu visualisieren, kann die Umgebungsvariable „ QSG_VISUALIZE “ auf einen der Werte gesetzt werden, die in den folgenden Abschnitten beschrieben werden. Wir stellen Beispiele für die Ausgabe einiger dieser Variablen mithilfe des folgenden QML-Codes bereit:
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
}
}
}
}Für den „ ListView “ auf der linken Seite setzen wir dessen Eigenschaft „ clip “ auf „ true “. Für den „ ListView “ auf der rechten Seite setzen wir zudem die Eigenschaft „ clip “ jedes Delegaten auf „ true “, um die Auswirkungen des Clippings auf das Batching zu veranschaulichen.

Original
Hinweis: Die visualisierten Elemente berücksichtigen keine Beschneidung, und die Darstellungsreihenfolge ist willkürlich.
Visualisierung von Batches
Wenn Sie „ QSG_VISUALIZE “ auf „ batches “ setzen, werden Batches im Renderer visualisiert. Zusammengeführte Batches werden mit einer durchgehenden Farbe dargestellt, nicht zusammengeführte Batches mit einem diagonalen Linienmuster. Wenige unterschiedliche Farben deuten auf gutes Batching hin. Nicht zusammengeführte Batches sind ungünstig, wenn sie viele einzelne Knoten enthalten.

QSG_VISUALIZE=batches
Visualisierung von Clipping
Wenn „ QSG_VISUALIZE “ auf „ clip “ gesetzt wird, werden rote Bereiche über der Szene gezeichnet, um Clipping anzuzeigen. Da „ Qt Quick “-Elemente standardmäßig kein Clipping durchführen, wird in der Regel kein Clipping visualisiert.

QSG_VISUALIZE=clip
Visualisierung von Änderungen
Wenn „ QSG_VISUALIZE “ auf „ changes “ gesetzt wird, werden Änderungen im Renderer visualisiert. Änderungen im Szenengraf werden durch eine blinkende Überlagerung in einer zufälligen Farbe dargestellt. Änderungen an einer Primitive werden mit einer Volltonfarbe visualisiert, während Änderungen an einem übergeordneten Element, wie z. B. Matrix- oder Opazitätsänderungen, mit einem Muster dargestellt werden.
Visualisierung von Overdraw
Wenn Sie „ QSG_VISUALIZE “ auf „ overdraw “ setzen, werden Übermalungen im Renderer visualisiert. Visualisieren Sie alle Elemente in 3D, um Übermalungen hervorzuheben. Dieser Modus kann in gewissem Umfang auch dazu verwendet werden, Geometrie außerhalb des Ansichtsfensters zu erkennen. Undurchsichtige Elemente werden mit einem grünen Farbton gerendert, während durchscheinende Elemente mit einem roten Farbton gerendert werden. Die Begrenzungsbox für den Ansichtsbereich wird blau dargestellt. Undurchsichtige Inhalte lassen sich vom Szenengrafen leichter verarbeiten und werden in der Regel schneller gerendert.
Beachten Sie, dass das Root-Rechteck im obigen Code überflüssig ist, da das Fenster ebenfalls weiß ist; das Zeichnen des Rechtecks ist in diesem Fall daher eine Verschwendung von Ressourcen. Die Umstellung auf ein „Item“ kann zu einer leichten Leistungssteigerung führen.


QSG_VISUALIZE=overdraw
Darstellung über das Qt Rendering Hardware Interface
Ab Qt 6.0 erfolgt die Darstellung standardmäßig immer über eine Grafikabstraktionsschicht, das Qt Rendering Hardware Interface (RHI), das vom Qt GUI Moduls bereitgestellt wird. Das bedeutet, dass im Gegensatz zu Qt 5 keine direkten OpenGL-Aufrufe durch den Szenengraphen erfolgen. Stattdessen erfasst dieser Ressourcen- und Zeichnungsbefehle mithilfe der RHI-APIs, die den Befehlsstrom anschließend in OpenGL-, Vulkan-, Metal- oder Direct 3D-Aufrufe übersetzen. Auch die Shader-Verarbeitung wird vereinheitlicht, indem Shader-Code einmal geschrieben, in SPIR-V kompiliert und anschließend in die für die verschiedenen Grafik-APIs geeignete Sprache übersetzt wird.
Zur Steuerung des Verhaltens können die folgenden Umgebungsvariablen verwendet werden:
| Umgebungsvariable | Mögliche Werte | Beschreibung |
|---|---|---|
QSG_RHI_BACKEND | vulkan, metal, opengl, d3d11, d3d12 | Fordert das spezifische RHI-Backend an. Standardmäßig wird die Ziel-Grafik-API basierend auf der Plattform ausgewählt, sofern dies nicht durch diese Variable oder die entsprechenden C++-APIs überschrieben wird. Die Standardwerte sind derzeit Direct3D 11 für Windows, Metal für macOS und OpenGL für alle anderen Systeme. |
QSG_INFO | 1 | Ähnlich wie beim OpenGL-basierten Rendering-Pfad ermöglicht die Einstellung dieser Option das Ausgeben von Systeminformationen bei der Initialisierung des „ Qt Quick “-Szenengraphen. Dies kann bei der Fehlerbehebung sehr hilfreich sein. |
QSG_RHI_DEBUG_LAYER | 1 | Soweit zutreffend (Vulkan, Direct3D), werden die Debug- oder Validierungs-Ebenen der Grafik-API-Implementierung aktiviert, sofern diese verfügbar sind, entweder auf dem Grafikgerät oder dem Instanzobjekt. Für Metal unter macOS setzen Sie stattdessen die Umgebungsvariable „ METAL_DEVICE_WRAPPER_TYPE=1 “. |
QSG_RHI_PREFER_SOFTWARE_RENDERER | 1 | Fordert die Auswahl eines Adapters oder physischen Geräts an, das softwarebasierte Rasterisierung verwendet. Gilt nur, wenn die zugrunde liegende API die Aufzählung von Adaptern unterstützt (z. B. Direct3D oder Vulkan); andernfalls wird dies ignoriert. |
Anwendungen, die stets mit einer bestimmten Grafik-API ausgeführt werden sollen, können dies ebenfalls über C++ anfordern. Beispielsweise erzwingt der folgende Aufruf, der früh in `main()` vor der Erstellung eines ` QQuickWindow` erfolgt, die Verwendung von Vulkan (und schlägt andernfalls fehl):
QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);Siehe QSGRendererInterface::GraphicsApi. Die Enum-Werte „ OpenGL “, „ Vulkan “, „ Metal “, „ Direct3D11 “ und „ Direct3D12 “ entsprechen in ihrer Wirkung der Ausführung mit „ QSG_RHI_BACKEND “, das auf den entsprechenden String-Schlüssel gesetzt ist.
Alle Backends unter QRhi wählen den vom System standardmäßig verwendeten GPU-Adapter oder das physische Gerät, sofern dies nicht durch „ QSG_RHI_PREFER_SOFTWARE_RENDERER “ oder eine backend-spezifische Variable wie beispielsweise „ QT_D3D_ADAPTER_INDEX “ oder „ QT_VK_PHYSICAL_DEVICE_INDEX “ überschrieben wird. Derzeit sind keine weiteren Konfigurationsmöglichkeiten für Adapter vorgesehen.
Ab Qt 6.5 sind einige der Einstellungen, die zuvor nur als Umgebungsvariablen verfügbar waren, nun als C++-APIs unter QQuickGraphicsConfiguration verfügbar. So sind beispielsweise die Einstellung von QSG_RHI_DEBUG_LAYER und der Aufruf von setDebugLayer(true) gleichbedeutend.
© 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.