Qt Quick Renderizador de gráficos de escena por defecto
Este documento explica cómo funciona internamente el renderizador de gráficos de escena por defecto, de forma que se pueda escribir código que lo utilice de forma óptima, tanto en rendimiento como en características.
No es necesario entender el funcionamiento interno del renderizador para obtener un buen rendimiento. Sin embargo, puede ayudar cuando se integra con el gráfico de escena o para averiguar por qué no es posible exprimir al máximo la eficiencia del chip gráfico.
Nota: Incluso en el caso de que cada fotograma sea único y todo se cargue desde cero, el renderizador por defecto tendrá un buen rendimiento.
Los elementos Qt Quick de una escena QML pueblan un árbol de instancias QSGNode. Una vez creado, este árbol es una descripción completa de cómo debe renderizarse un determinado fotograma. No contiene ninguna referencia a los elementos de Qt Quick y, en la mayoría de las plataformas, se procesará y renderizará en un subproceso independiente. El renderizador es una parte autónoma del gráfico de escena que recorre el árbol QSGNode y utiliza la geometría definida en QSGGeometryNode y el estado del sombreador definido en QSGMaterial para actualizar el estado de los gráficos y generar llamadas de dibujo.
En caso necesario, el renderizador puede sustituirse por completo utilizando la API interna de back-end del grafo de escena. Esto es interesante sobre todo para los proveedores de plataformas que deseen aprovechar características de hardware no estándar. Para la mayoría de los casos de uso, el renderizador por defecto será suficiente.
El renderizador por defecto se centra en dos estrategias principales para optimizar el renderizado: El procesamiento por lotes de las llamadas a dibujo y la retención de la geometría en la GPU.
Batching
Mientras que una API 2D tradicional, como QPainter, Cairo o Context2D, está escrita para gestionar miles de llamadas de dibujo individuales por fotograma, OpenGL y otras API aceleradas por hardware funcionan mejor cuando el número de llamadas de dibujo es muy bajo y los cambios de estado se mantienen al mínimo.
Nota: Aunque OpenGL se utiliza como ejemplo en las siguientes secciones, los mismos conceptos se aplican también a otras APIs gráficas.
Considera el siguiente caso de uso:

La forma más sencilla de dibujar esta lista es celda por celda. En primer lugar, se dibuja el fondo. Se trata de un rectángulo de un color determinado. En términos de OpenGL, esto significa seleccionar un programa de sombreado para hacer rellenos de color sólido, establecer el color de relleno, establecer la matriz de transformación que contiene los desplazamientos x e y y, a continuación, utilizar, por ejemplo, glDrawArrays para dibujar dos triángulos que componen el rectángulo. A continuación se dibuja el icono. En términos de OpenGL, esto significa seleccionar un programa de sombreado para dibujar texturas, seleccionar la textura activa que se va a utilizar, establecer la matriz de transformación, activar la mezcla alfa y, a continuación, utilizar, por ejemplo, glDrawArrays para dibujar los dos triángulos que forman el rectángulo que delimita el icono. El texto y la línea de separación entre celdas siguen un patrón similar. Y este proceso se repite para cada celda de la lista, por lo que para una lista más larga, la sobrecarga impuesta por los cambios de estado de OpenGL y las llamadas de dibujo supera completamente el beneficio que podría proporcionar el uso de una API acelerada por hardware.
Cuando cada primitiva es grande, esta sobrecarga es insignificante, pero en el caso de una interfaz de usuario típica, hay muchos elementos pequeños que suman una sobrecarga considerable.
El renderizador de gráficos de escena predeterminado trabaja dentro de estas limitaciones e intentará fusionar primitivas individuales en lotes conservando exactamente el mismo resultado visual. El resultado es un menor número de cambios de estado OpenGL y una cantidad mínima de llamadas a dibujo, lo que se traduce en un rendimiento óptimo.
Primitivas opacas
El renderizador distingue entre primitivas opacas y primitivas que requieren mezcla alfa. Al utilizar el búfer Z de OpenGL y dar a cada primitiva una posición z única, el renderizador puede reordenar libremente las primitivas opacas sin tener en cuenta su ubicación en la pantalla ni con qué otros elementos se solapan. Observando el estado del material de cada primitiva, el renderizador creará lotes opacos. En el conjunto de elementos principales de Qt Quick, esto incluye elementos Rectángulo con colores opacos e imágenes totalmente opacas, como JPEG o BMP.
Otra ventaja de utilizar primitivas opacas es que éstas no requieren que se habilite GL_BLEND, lo que puede resultar bastante costoso, especialmente en GPU móviles e integradas.
Las primitivas opacas se renderizan de adelante hacia atrás con glDepthMask y GL_DEPTH_TEST activados. En las GPUs que internamente realizan comprobaciones early-z, esto significa que el fragment shader no necesita ejecutarse para los píxeles o bloques de píxeles que están oscurecidos. Ten en cuenta que el renderizador todavía necesita tener en cuenta estos nodos y el sombreador de vértices todavía se ejecuta para cada vértice en estas primitivas, por lo que si la aplicación sabe que algo está totalmente oscurecido, lo mejor es ocultarlo explícitamente usando Item::visible o Item::opacity.
Nota: Item::z se utiliza para controlar el orden de apilamiento de un elemento con respecto a sus hermanos. No tiene relación directa con el renderizador y el búfer Z de OpenGL.
Primitivas con mezcla alfa
Una vez que las primitivas opacas han sido dibujadas, el renderizador deshabilitará glDepthMask, habilitará GL_BLEND y renderizará todas las primitivas de mezcla alfa de manera sucesiva.
La mezcla de primitivas alfa requiere un poco más de esfuerzo en el renderizador ya que los elementos que se superponen necesitan ser renderizados en el orden correcto para que la mezcla alfa se vea correctamente. Confiar únicamente en el búfer Z no es suficiente. El renderizador hace una pasada por todas las primitivas mezcladas alfa y mirará sus rectos delimitadores además del estado de sus materiales para averiguar qué elementos pueden ser mezclados y cuáles no.

En el caso más a la izquierda, los fondos azules se pueden dibujar en una llamada y los dos elementos de texto en otra llamada, ya que los textos sólo se superponen a un fondo delante del cual se apilan. En el caso más a la derecha, el fondo del "Elemento 4" se superpone al texto del "Elemento 3", por lo que en este caso, cada uno de los fondos y textos necesita ser dibujado usando llamadas separadas.
Con respecto a Z, las primitivas alfa se intercalan con los nodos opacos y pueden activar early-z cuando estén disponibles, pero de nuevo, establecer Item::visible a false es siempre más rápido.
Mezcla con primitivas 3D
El gráfico de escena puede soportar primitivas pseudo 3D y 3D propiamente dichas. Por ejemplo, se puede implementar un efecto "page curl" utilizando ShaderEffect o implementar un toroide con bumpmap utilizando QSGGeometry y un material personalizado. Al hacerlo, hay que tener en cuenta que el renderizador por defecto ya hace uso del búfer de profundidad.
El renderizador modifica el sombreador de vértices devuelto por QSGMaterialShader::vertexShader() y comprime los valores z del vértice después de que se hayan aplicado las matrices model-view y projection y luego añade una pequeña traslación en la z para posicionarla en la posición z correcta.
La compresión asume que los valores z están en el rango de 0 a 1.
Atlas de texturas
La textura activa es un estado OpenGL único, lo que significa que no se pueden agrupar por lotes varias primitivas que utilicen diferentes texturas OpenGL. El gráfico de escena Qt Quick, por esta razón, permite que múltiples instancias de QSGTexture sean asignadas como sub-regiones más pequeñas de una textura más grande; un atlas de textura.
La mayor ventaja de los atlas de texturas es que varias instancias de QSGTexture se refieren ahora a la misma instancia de textura OpenGL. Esto hace posible también las llamadas de dibujo por lotes de texturas, como elementos Image, elementos BorderImage, elementos ShaderEffect y también tipos C++ como QSGSimpleTextureNode y QSGGeometryNodes personalizados que utilizan texturas.
Nota: Las texturas grandes no van al atlas de texturas.
Las texturas basadas en atlas se crean pasando QQuickWindow::TextureCanUseAtlas a la función QQuickWindow::createTextureFromImage().
Nota : Las texturas basadas en atlas no tienen coordenadas de textura que vayan de 0 a 1. Utilice QSGTexture::normalizedTextureSubRect() para obtener las coordenadas de textura del atlas.
El gráfico de escena utiliza la heurística para averiguar cómo de grande debe ser el atlas y cuál es el umbral de tamaño para ser introducido en el atlas. Si se necesitan valores diferentes, es posible anularlos utilizando las variables de entorno QSG_ATLAS_WIDTH=[width], QSG_ATLAS_HEIGHT=[height] y QSG_ATLAS_SIZE_LIMIT=[size]. La modificación de estos valores interesará sobre todo a los proveedores de plataformas.
Raíces por lotes
Además de combinar primitivas compatibles en lotes, el renderizador por defecto también intenta minimizar la cantidad de datos que deben enviarse a la GPU por cada fotograma. El renderizador por defecto identifica los subárboles que van juntos e intenta colocarlos en lotes separados. Una vez identificados los lotes, se fusionan, se cargan y se almacenan en la memoria de la GPU, utilizando Vertex Buffer Objects.
Nodos de transformación
Cada elemento de Qt Quick inserta un QSGTransformNode en el árbol gráfico de la escena para gestionar su x, y, escala o rotación. Los ítems hijos serán poblados bajo este nodo de transformación. El renderizador por defecto rastrea el estado de los nodos de transformación entre fotogramas y mirará los subárboles para decidir si un nodo de transformación es un buen candidato para convertirse en raíz de un conjunto de lotes. Un nodo de transformación que cambia entre fotogramas y que tiene un subárbol bastante complejo puede convertirse en raíz de un lote.
Los QSGGeometryNodes del subárbol de una raíz de lote se pretransforman en relación con la raíz en la CPU. A continuación, se cargan y retienen en la GPU. Cuando cambia la transformación, el renderizador sólo necesita actualizar la matriz de la raíz, no cada elemento individual, lo que agiliza enormemente el desplazamiento por listas y cuadrículas. En los fotogramas sucesivos, mientras no se añadan o eliminen nodos, el renderizado de la lista es efectivamente gratuito. Cuando entra nuevo contenido en el subárbol, se reconstruye el lote que lo recibe, pero esto sigue siendo relativamente rápido. Normalmente hay varios fotogramas sin cambios por cada fotograma con nodos añadidos o eliminados cuando se recorre una cuadrícula o lista.
Otra ventaja de identificar los nodos de transformación como raíces del lote es que permite al renderizador conservar las partes del árbol que no han cambiado. Por ejemplo, supongamos que una interfaz de usuario consiste en una lista y una fila de botones. Cuando la lista se desplaza y se añaden y eliminan delegados, el resto de la interfaz de usuario, la fila de botones, no cambia y puede dibujarse utilizando la geometría ya almacenada en la GPU.
El umbral de nodos y vértices para que un nodo de transformación se convierta en una raíz de lote puede anularse utilizando las variables de entorno QSG_RENDERER_BATCH_NODE_THRESHOLD=[count] y QSG_RENDERER_BATCH_VERTEX_THRESHOLD=[count]. La anulación de estas banderas será útil sobre todo para los proveedores de plataformas.
Nota: Bajo una raíz de lote, se crea un lote para cada conjunto único de estado de material y tipo de geometría.
Recorte
Cuando se establece Item::clip a true, se creará un QSGClipNode con un rectángulo en su geometría. El renderizador por defecto aplicará este recorte utilizando tijeras en OpenGL. Si el item es rotado en un ángulo que no sea de 90 grados, se utilizará el stencil buffer de OpenGL. Qt Quick Item sólo permite establecer un rectángulo como recorte a través de QML, pero la API de gráficos de escena y el renderizador por defecto pueden utilizar cualquier forma para el recorte.
Cuando se aplica un clip a un subárbol, ese subárbol necesita ser renderizado con un estado OpenGL único. Esto significa que cuando Item::clip es verdadero, el procesamiento por lotes de ese elemento se limita a sus hijos. Cuando hay muchos hijos, como un ListView o GridView, o hijos complejos, como un TextArea, esto está bien. Sin embargo, hay que tener cuidado al utilizar clip en elementos más pequeños, ya que impide la agrupación por lotes. Esto incluye la etiqueta de un botón, el delegado de un campo de texto o de una lista y las celdas de una tabla. El recorte de un Flickable (o vista de elemento) puede evitarse a menudo organizando la interfaz de usuario de modo que los elementos opacos cubran las áreas alrededor del Flickable, y confiando en los bordes de la ventana para recortar todo lo demás.
Establecer Item::clip a true también establece la bandera QQuickItem::ItemIsViewport; los elementos hijos con la bandera QQuickItem::ItemObservesViewport pueden utilizar la ventana gráfica para un paso previo de recorte aproximado: por ejemplo, Text omite las líneas de texto que están completamente fuera de la ventana gráfica. La omisión de nodos del gráfico de escena o la limitación de vertices es una optimización, que puede conseguirse configurando flags en C++ en lugar de Item::clip en QML.
Al implementar QQuickItem::updatePaintNode() en un elemento personalizado, si puede renderizar muchos detalles sobre un área geométrica grande, deberías pensar si es eficiente limitar los gráficos a la ventana gráfica; si es así, puedes establecer la bandera ItemObservesViewport y leer el área actualmente expuesta desde QQuickItem::clipRect(). Una consecuencia es que updatePaintNode() se llamará más a menudo (normalmente una vez por fotograma siempre que el contenido se esté moviendo en la ventana gráfica).
Búferes de vértices
Cada lote utiliza un objeto de búfer de vértices (VBO) para almacenar sus datos en la GPU. Este búfer de vértices se conserva entre fotogramas y se actualiza cuando cambia la parte del gráfico de la escena que representa.
Por defecto, el renderizador cargará los datos en el VBO utilizando GL_STATIC_DRAW. Es posible seleccionar una estrategia de carga diferente configurando la variable de entorno QSG_RENDERER_BUFFER_STRATEGY=[strategy]. Los valores válidos son stream y dynamic. Cambiar este valor es útil sobre todo para los proveedores de plataformas.
Antialiasing
El gráfico de escena soporta dos tipos de antialiasing. Por defecto, las primitivas como rectángulos e imágenes serán antialiasing añadiendo más vértices a lo largo del borde de las primitivas para que los bordes se desvanezcan hasta hacerse transparentes. A este método lo llamamos antialiasing de vértices. Si el usuario solicita un contexto OpenGL multimuestreo, estableciendo un QSurfaceFormat con muestras superiores a 0 utilizando QQuickWindow::setFormat(), el gráfico de la escena preferirá el antialiasing basado en multimuestreo (MSAA). Las dos técnicas afectarán a la forma en que se produce el renderizado internamente y tienen diferentes limitaciones.
También es posible anular el método de antialiasing utilizado estableciendo la variable de entorno QSG_ANTIALIASING_METHOD a vertex o msaa.
El antialiasing de vértices puede producir costuras entre bordes de primitivas adyacentes, incluso cuando los dos bordes son matemáticamente iguales. El antialiasing multimuestra no lo hace.
Antialiasing de vértices
El antialiasing de vértices puede activarse y desactivarse por elemento mediante la propiedad Item::antialiasing. Funcionará independientemente de lo que soporte el hardware subyacente y produce un antialiasing de mayor calidad, tanto para primitivas renderizadas normalmente como para primitivas capturadas en objetos framebuffer, por ejemplo usando el tipo ShaderEffectSource.
La desventaja de utilizar el antialiasing de vértices es que cada primitiva con antialiasing activado tendrá que ser mezclada. En términos de procesamiento por lotes, esto significa que el renderizador tiene que hacer más trabajo para averiguar si la primitiva se puede mezclar o no y, debido a los solapamientos con otros elementos de la escena, también puede dar lugar a un menor procesamiento por lotes, lo que podría afectar al rendimiento.
En hardware de gama baja, la mezcla también puede ser bastante costosa, por lo que para una imagen o rectángulo redondeado que cubra la mayor parte de la pantalla, la cantidad de mezcla necesaria para el interior de estas primitivas puede resultar en una pérdida de rendimiento significativa, ya que toda la primitiva debe ser mezclada.
Antialiasing multimuestra
El antialiasing multimuestra es una función del hardware que calcula un valor de cobertura por píxel en la primitiva. Algunos equipos pueden realizar multimuestreo a un coste muy bajo, mientras que otros pueden necesitar más memoria y más ciclos de GPU para renderizar un fotograma.
Con el antialiasing multimuestra, muchas primitivas, como rectángulos redondeados y elementos de imagen, pueden antialiasearse y seguir siendo opacas en el gráfico de la escena. Esto significa que el renderizador tiene un trabajo más fácil al crear lotes y puede confiar en early-z para evitar el sobredibujo.
Cuando se utiliza el antialiasing multimuestreo, el contenido renderizado en objetos framebuffer necesita extensiones adicionales para soportar el multimuestreo de framebuffers. Normalmente GL_EXT_framebuffer_multisample y GL_EXT_framebuffer_blit. La mayoría de los chips de sobremesa disponen de estas extensiones, pero son menos comunes en los chips integrados. Cuando el hardware no dispone de multimuestreo de framebuffer, el contenido renderizado en objetos framebuffer no tendrá antialiasing, incluido el contenido de ShaderEffectSource.
Rendimiento
Como se dijo al principio, no es necesario comprender los detalles más sutiles del renderizador para obtener un buen rendimiento. Está escrito para optimizar los casos de uso comunes y funcionará bastante bien en casi cualquier circunstancia.
- Un buen rendimiento viene de un batching efectivo, con la menor cantidad posible de geometría siendo cargada una y otra vez. Estableciendo la variable de entorno
QSG_RENDERER_DEBUG=render, el renderizador mostrará estadísticas sobre cómo va la dosificación, cuántos lotes se utilizan, qué lotes se retienen y cuáles son opacos y cuáles no. Cuando se busca un rendimiento óptimo, las cargas deberían ocurrir sólo cuando son realmente necesarias, los lotes deberían ser menos de 10 y al menos 3-4 de ellos deberían ser opacos. - El renderizador por defecto no hace ningún recorte de vista del lado de la CPU ni detección de oclusión. Si algo no debe ser visible, no debe mostrarse. Utilice
Item::visible: falsepara los elementos que no deben ser dibujados. La razón principal para no añadir dicha lógica es que añade un coste adicional que también perjudicaría a las aplicaciones que se preocuparan por comportarse bien. - Asegúrese de que se utiliza el atlas de texturas. Los elementos Image y BorderImage lo utilizarán a menos que la imagen sea demasiado grande. Para texturas creadas en C++, pasa QQuickWindow::TextureCanUseAtlas cuando llames a QQuickWindow::createTexture(). Estableciendo la variable de entorno
QSG_ATLAS_OVERLAYtodas las texturas atlas serán coloreadas para que sean fácilmente identificables en la aplicación. - Utilice primitivas opacas siempre que sea posible. Las primitivas opacas son más rápidas de procesar en el renderizador y más rápidas de dibujar en la GPU. Por ejemplo, los archivos PNG suelen tener un canal alfa, aunque cada píxel sea totalmente opaco. Los archivos JPG son siempre opacos. Cuando proporcione imágenes a QQuickImageProvider o cree imágenes con QQuickWindow::createTextureFromImage(), deje que la imagen tenga QImage::Format_RGB32, cuando sea posible.
- Tenga en cuenta que los elementos compuestos superpuestos, como en la ilustración anterior, no se pueden agrupar por lotes.
- El recorte rompe la dosificación. Nunca lo utilice por artículo, dentro de celdas de tabla, delegados de artículo o similares. En lugar de recortar texto, utilice elidir. En lugar de recortar una imagen, cree un QQuickImageProvider que devuelva una imagen recortada.
- La agrupación sólo funciona con índices de 16 bits. Todos los elementos incorporados utilizan índices de 16 bits, pero una geometría personalizada es libre de utilizar también índices de 32 bits.
- Algunos indicadores de material impiden el procesamiento por lotes, siendo el más limitante QSGMaterial::RequiresFullMatrix, que impide todo procesamiento por lotes.
- Las aplicaciones con un fondo monocromo deben establecerlo utilizando QQuickWindow::setColor() en lugar de utilizar un elemento Rectángulo de nivel superior. QQuickWindow::setColor() se utilizará en una llamada a
glClear(), que es potencialmente más rápida. - Los elementos Mipmapped Image no se colocan en el atlas global y no se agruparán por lotes.
- Un error en el controlador OpenGL relacionado con la lectura de objetos de memoria intermedia (FBO) puede corromper los glifos renderizados. Si se establece la variable de entorno
QML_USE_GLYPHCACHE_WORKAROUND, Qt mantiene una copia adicional del glifo en RAM. Esto significa que el rendimiento es ligeramente inferior cuando se dibujan glifos que no se han dibujado antes, ya que Qt accede a la copia extra a través de la CPU. También significa que la caché del glifo utilizará el doble de memoria. La calidad no se ve afectada por esto.
Si una aplicación funciona mal, asegúrate de que el renderizado es realmente el cuello de botella. Utiliza un perfilador. La variable de entorno QSG_RENDER_TIMING=1 mostrará una serie de parámetros de temporización que pueden ser útiles para localizar el problema.
Visualización de
Para visualizar los diversos aspectos del renderizador por defecto del gráfico de escena, la variable de entorno QSG_VISUALIZE puede establecerse en uno de los valores detallados en cada sección a continuación. Proporcionamos ejemplos de la salida de algunas de las variables utilizando el siguiente código QML:
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 } } } }
Para el ListView de la izquierda, establecemos su propiedad clip en true. Para el ListView de la derecha, también establecemos la propiedad clip de cada delegado en true para ilustrar los efectos del recorte en la dosificación.

Original
Nota: Los elementos visualizados no respetan el recorte, y el orden de representación es arbitrario.
Visualización de lotes
Configurando QSG_VISUALIZE a batches se visualizan los lotes en el renderizador. Los lotes fusionados se dibujan con un color sólido y los lotes no fusionados se dibujan con un patrón de líneas diagonales. Pocos colores únicos significa un buen loteado. Los lotes no fusionados son malos si contienen muchos nodos individuales.

QSG_VISUALIZE=batches
Visualización del recorte
Configurando QSG_VISUALIZE a clip dibuja áreas rojas en la parte superior de la escena para indicar recorte. Como los elementos de Qt Quick no se recortan por defecto, normalmente no se visualiza ningún recorte.

QSG_VISUALIZE=clip
Visualización de cambios
Configurando QSG_VISUALIZE a changes se visualizan los cambios en el renderizador. Los cambios en el scenegraph se visualizan con una superposición intermitente de un color aleatorio. Los cambios en una primitiva se visualizan con un color sólido, mientras que los cambios en un ancestro, como cambios de matriz u opacidad, se visualizan con un patrón.
Visualización de la sobreimpresión
Configurando QSG_VISUALIZE a overdraw se visualiza la sobretraza en el renderizador. Visualiza todos los elementos en 3D para resaltar los sobredibujos. Este modo también puede utilizarse para detectar geometría fuera de la ventana gráfica hasta cierto punto. Los elementos opacos se renderizan con un tinte verde, mientras que los translúcidos se renderizan con un tinte rojo. El cuadro delimitador de la ventana gráfica se representa en azul. El contenido opaco es más fácil de procesar para el scenegraph y suele ser más rápido de renderizar.
Tenga en cuenta que el rectángulo raíz en el código anterior es superfluo ya que la ventana también es blanca, por lo que dibujar el rectángulo es un desperdicio de recursos en este caso. Cambiarlo por un elemento puede aumentar ligeramente el rendimiento.


QSG_VISUALIZE=overdraw
Renderizado a través del Qt Rendering Hardware Interface
Desde Qt 6.0 en adelante, la adaptación por defecto siempre renderiza a través de una capa de abstracción gráfica, la Qt Rendering Hardware Interface (RHI), proporcionada por el módulo Qt GUI de Qt. Esto significa que, a diferencia de Qt 5, el gráfico de escena no realiza llamadas directas a OpenGL. En su lugar, registra los recursos y los comandos de dibujo mediante las API de RHI, que luego traducen el flujo de comandos en llamadas OpenGL, Vulkan, Metal o Direct 3D. El manejo de los sombreadores también se unifica escribiendo el código de los sombreadores una vez, compilándolo a SPIR-V y traduciéndolo después al lenguaje apropiado para las distintas API de gráficos.
Para controlar el comportamiento, se pueden utilizar las siguientes variables de entorno:
| Variable de entorno | Valores posibles | Descripción |
|---|---|---|
QSG_RHI_BACKEND | vulkan, metal, opengl, d3d11, d3d12 | Solicita el backend RHI específico. Por defecto, la API de gráficos elegida se basa en la plataforma, a menos que sea reemplazada por esta variable o las APIs C++ equivalentes. Los valores por defecto son actualmente Direct3D 11 para Windows, Metal para macOS, OpenGL en otros lugares. |
QSG_INFO | 1 | Al igual que con la ruta de renderizado basada en OpenGL, esta opción permite imprimir información del sistema al inicializar el gráfico de escena Qt Quick. Esto puede ser muy útil para la resolución de problemas. |
QSG_RHI_DEBUG_LAYER | 1 | Cuando corresponda (Vulkan, Direct3D), activa las capas de depuración o validación de la implementación de la API de gráficos, si están disponibles, ya sea en el dispositivo gráfico o en el objeto de instancia. Para Metal en macOS, establezca la variable de entorno METAL_DEVICE_WRAPPER_TYPE=1 en su lugar. |
QSG_RHI_PREFER_SOFTWARE_RENDERER | 1 | Solicita elegir un adaptador o dispositivo físico que utilice rasterización basada en software. Aplicable sólo cuando la API subyacente tiene soporte para enumerar adaptadores (por ejemplo, Direct3D o Vulkan), y se ignora en caso contrario. |
Las aplicaciones que deseen ejecutarse siempre con una única API gráfica determinada, pueden solicitarlo también a través de C++. Por ejemplo, la siguiente llamada realizada al principio de main(), antes de construir cualquier QQuickWindow, fuerza el uso de Vulkan (y fallará en caso contrario):
QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);
Ver QSGRendererInterface::GraphicsApi. Los valores enum OpenGL, Vulkan, Metal, Direct3D11, Direct3D12 son equivalentes en efecto a ejecutar con QSG_RHI_BACKEND establecido a la clave de cadena equivalente.
Todos los backends QRhi elegirán el adaptador GPU por defecto del sistema o el dispositivo físico, a menos que sea anulado por QSG_RHI_PREFER_SOFTWARE_RENDERER o una variable específica del backend, como QT_D3D_ADAPTER_INDEX o QT_VK_PHYSICAL_DEVICE_INDEX. No se proporciona ninguna otra configurabilidad del adaptador en este momento.
A partir de Qt 6.5, algunos de los ajustes que antes sólo se exponían como variables de entorno están disponibles como API de C++ en QQuickGraphicsConfiguration. Por ejemplo, configurar QSG_RHI_DEBUG_LAYER y llamar a setDebugLayer(true) son equivalentes.
© 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.