En esta página

Qt Quick Gráfico de escena

El gráfico de escena en Qt Quick

Qt Quick 2 utiliza un gráfico de escena dedicado que se recorre y renderiza a través de una API de gráficos como OpenGL ES, OpenGL, Vulkan, Metal o Direct 3D. El uso de un gráfico de escena para los gráficos, en lugar de los sistemas de pintura imperativos tradicionales (QPainter y similares), significa que la escena que se va a renderizar puede conservarse entre fotogramas y que el conjunto completo de primitivas que se van a renderizar se conoce antes de que comience la renderización. Esto permite una serie de optimizaciones, como el renderizado por lotes para minimizar los cambios de estado y descartar las primitivas oscurecidas.

Por ejemplo, supongamos que una interfaz de usuario contiene una lista de diez elementos, cada uno de los cuales tiene un color de fondo, un icono y un texto. Utilizando las técnicas de dibujo tradicionales, esto daría lugar a 30 llamadas a dibujo y una cantidad similar de cambios de estado. Un gráfico de escena, en cambio, podría reorganizar las primitivas a renderizar de forma que todos los fondos se dibujasen en una llamada, luego todos los iconos y después todo el texto, reduciendo la cantidad total de llamadas de dibujo a sólo 3. La agrupación por lotes y la reducción de cambios de estado como ésta pueden mejorar enormemente el rendimiento en algunos equipos.

El gráfico de escena está estrechamente vinculado a Qt Quick 2.0 y no puede utilizarse de forma independiente. El gráfico de escena es gestionado y renderizado por la clase QQuickWindow y los tipos de elementos personalizados pueden añadir sus primitivas gráficas al gráfico de escena mediante una llamada a QQuickItem::updatePaintNode().

El gráfico de escena es una representación gráfica de la escena del objeto, una estructura independiente que contiene información suficiente para representar todos los objetos. Una vez configurado, puede ser manipulado y renderizado independientemente del estado de los ítems. En muchas plataformas, el gráfico de escena incluso se renderiza en un subproceso de renderizado dedicado mientras el subproceso de la interfaz gráfica de usuario prepara el estado del siguiente fotograma.

Nota: Gran parte de la información que aparece en esta página es específica del comportamiento incorporado por defecto del gráfico de escena de Qt Quick. Cuando se utiliza una adaptación alternativa del grafo de escena, como la adaptación software, puede que no se apliquen todos los conceptos. Para obtener más información sobre las diferentes adaptaciones del gráfico de escena, consulte Adaptaciones del gráfico de escena.

Qt Quick Estructura del gráfico de escena

El grafo de escena se compone de una serie de tipos de nodos predefinidos, cada uno de los cuales tiene una función específica. Aunque nos referimos a él como gráfico de escena, una definición más precisa es árbol de nodos. El árbol se construye a partir de los tipos de QQuickItem en la escena QML e internamente la escena es procesada por un renderizador que dibuja la escena. Los nodos en sí no contienen ningún código de dibujo activo ni función virtual paint().

Aunque el árbol de nodos se construye en su mayor parte internamente a partir de los tipos QML existentes en Qt Quick, los usuarios también pueden añadir subárboles completos con su propio contenido, incluidos subárboles que representen modelos 3D.

Nodos

El nodo más importante para los usuarios es QSGGeometryNode. Se utiliza para definir gráficos personalizados definiendo su geometría y material. La geometría se define mediante QSGGeometry y describe la forma o malla de la primitiva gráfica. Puede ser una línea, un rectángulo, un polígono, muchos rectángulos desconectados o una malla 3D compleja. El material define cómo se rellenan los píxeles de esta forma.

Un nodo puede tener cualquier número de hijos y los nodos geométricos se renderizarán de forma que aparezcan en orden de hijo con los padres detrás de sus hijos.

Nota: Esto no dice nada sobre el orden real de renderizado en el renderizador. Sólo se garantiza el resultado visual.

Los nodos disponibles son:

QSGClipNode

Implementa la funcionalidad de recorte en el gráfico de escena.

QSGGeometryNode

Se utiliza para todo el contenido renderizado en el gráfico de escena

QSGNode

Clase base para todos los nodos del gráfico de escena

QSGOpacityNode

Se utiliza para cambiar la opacidad de los nodos

QSGTransformNode

Implementa transformaciones en el gráfico de escena

Los nodos personalizados se añaden al gráfico de escena subclasificando QQuickItem::updatePaintNode() y estableciendo la bandera QQuickItem::ItemHasContents.

Advertencia: Es crucial que las operaciones gráficas nativas (OpenGL, Vulkan, Metal, etc.) y la interacción con el gráfico de escena ocurran exclusivamente en el hilo de renderizado, principalmente durante la llamada updatePaintNode(). La regla general es utilizar únicamente clases con el prefijo "QSG" dentro de la función QQuickItem::updatePaintNode().

Para obtener más detalles, consulte el Gráfico de escena - Geometría personalizada.

Preprocesamiento

Los nodos tienen una función virtual QSGNode::preprocess(), que será llamada antes de que el gráfico de la escena sea renderizado. Las subclases de nodos pueden establecer la bandera QSGNode::UsePreprocess y anular la función QSGNode::preprocess() para realizar la preparación final de su nodo. Por ejemplo, dividir una curva bezier en el nivel correcto de detalle para el factor de escala actual o actualizar una sección de una textura.

Propiedad del nodo

La propiedad de los nodos se asigna explícitamente por el creador o por el gráfico de la escena mediante la bandera QSGNode::OwnedByParent. A menudo es preferible asignar la propiedad al gráfico de la escena, ya que simplifica la limpieza cuando el gráfico de la escena vive fuera del hilo de la GUI.

Materiales

El material describe cómo se rellena el interior de una geometría en QSGGeometryNode. Encapsula sombreadores gráficos para las etapas de vértices y fragmentos del proceso gráfico y proporciona una amplia flexibilidad en lo que se puede lograr, aunque la mayoría de los elementos de Qt Quick sólo utilizan materiales muy básicos, como el color sólido y los rellenos de textura.

Los usuarios que sólo deseen aplicar un sombreado personalizado a un tipo de elemento QML pueden hacerlo directamente en QML utilizando el tipo ShaderEffect.

A continuación se muestra una lista completa de clases de materiales:

QSGFlatColorMaterial

Una forma cómoda de renderizar geometría de color sólido en el gráfico de escena

QSGMaterial

Encapsula el estado de renderizado para un programa de sombreado

QSGMaterialShader

Representa un programa de sombreado independiente de la API gráfica

QSGMaterialType

Se utiliza como token de tipo único en combinación con QSGMaterial

QSGOpaqueTextureMaterial

Forma práctica de renderizar geometría texturizada en el gráfico de escena

QSGTextureMaterial

Manera conveniente de renderizar geometría texturizada en el gráfico de escena

QSGVertexColorMaterial

Cómo renderizar geometría coloreada por vértice en el gráfico de escena

Nodos convenientes

La API del gráfico de escena es de bajo nivel y se centra más en el rendimiento que en la comodidad. Escribir geometrías y materiales personalizados desde cero, incluso los más básicos, requiere una cantidad de código no trivial. Por esta razón, la API incluye algunas clases de conveniencia para hacer que los nodos personalizados más comunes estén fácilmente disponibles.

Gráfico de escena y renderizado

El renderizado del gráfico de escena se realiza internamente en la clase QQuickWindow, y no existe una API pública para acceder a él. Hay, sin embargo, algunos lugares en el proceso de renderizado donde el usuario puede adjuntar código de aplicación. Esto puede utilizarse para añadir contenido personalizado al gráfico de escena o para insertar comandos de renderizado arbitrarios llamando directamente a la API de gráficos (OpenGL, Vulkan, Metal, etc.) utilizada por el gráfico de escena. Los puntos de integración están definidos por el bucle de renderizado.

Para obtener una descripción detallada de cómo funciona el renderizador de gráficos de escena, consulte Qt Quick Scene Graph Default Renderer.

Hay dos variantes de render loop disponibles: basic, y threaded. basic es monohilo, mientras que threaded realiza el renderizado del gráfico de escena en un hilo dedicado. Qt intenta elegir un bucle adecuado basándose en la plataforma y posiblemente en los controladores gráficos en uso. Cuando esto no es satisfactorio, o con fines de prueba, la variable de entorno QSG_RENDER_LOOP se puede utilizar para forzar el uso de un bucle determinado. Para verificar qué bucle de renderizado se está utilizando, active la variable de entorno qt.scenegraph.general logging category .

Bucle de renderizado roscado ('roscado')

En muchas configuraciones, el renderizado del gráfico de la escena tendrá lugar en un hilo de renderizado dedicado. Esto se hace para aumentar el paralelismo de los procesadores multinúcleo y hacer un mejor uso de los tiempos de espera, tales como la espera de una llamada de bloqueo al búfer de intercambio. Esto ofrece importantes mejoras de rendimiento, pero impone ciertas restricciones sobre dónde y cuándo se puede interactuar con el gráfico de la escena.

A continuación se muestra un esquema sencillo de cómo se renderiza un fotograma con el bucle de renderizado enhebrado y OpenGL. Los pasos son los mismos con otras APIs gráficas también, aparte de los específicos del contexto OpenGL.

  1. Se produce un cambio en la escena QML que provoca la llamada a QQuickItem::update(). Esto puede ser el resultado de, por ejemplo, una animación o una entrada del usuario. Se envía un evento al subproceso de renderizado para iniciar un nuevo fotograma.
  2. El hilo de renderizado se prepara para dibujar un nuevo fotograma e inicia un bloque en el hilo GUI.
  3. Mientras el hilo de renderizado prepara el nuevo fotograma, el hilo GUI llama a QQuickItem::updatePolish() para hacer el retoque final de los elementos antes de que sean renderizados.
  4. El hilo GUI se bloquea.
  5. Se emite la señal QQuickWindow::beforeSynchronizing(). Las aplicaciones pueden realizar conexiones directas (utilizando Qt::DirectConnection) a esta señal para realizar cualquier preparación necesaria antes de las llamadas a QQuickItem::updatePaintNode().
  6. Sincronización del estado QML en el gráfico de escena. Esto se hace llamando a la función QQuickItem::updatePaintNode() en todos los elementos que han cambiado desde el fotograma anterior. Esta es la única vez que los elementos QML y los nodos del gráfico de escena interactúan.
  7. Se libera el bloque del subproceso GUI.
  8. Se renderiza el gráfico de escena:
    1. Se emite la señal QQuickWindow::beforeRendering(). Las aplicaciones pueden establecer conexiones directas (mediante Qt::DirectConnection) con esta señal para utilizar llamadas personalizadas a la API de gráficos que se apilarán visualmente bajo la escena QML.
    2. Los elementos que hayan especificado QSGNode::UsePreprocess, tendrán su función QSGNode::preprocess() invocada.
    3. El renderizador procesa los nodos.
    4. El renderizador genera estados y registra llamadas a dibujo para la API gráfica en uso.
    5. Se emite la señal QQuickWindow::afterRendering(). Las aplicaciones pueden realizar conexiones directas (utilizando Qt::DirectConnection) a esta señal para emitir llamadas personalizadas a la API de gráficos que luego se apilarán visualmente sobre la escena QML.
    6. El fotograma ya está listo. Los búferes se intercambian (OpenGL), o se registra un comando presente y los búferes de comandos se envían a una cola de gráficos (Vulkan, Metal). QQuickWindow::frameSwapped() se emite.
  9. Mientras el hilo de renderizado está renderizando, la GUI está libre para avanzar animaciones, procesar eventos, etc.

El renderizador por hilos se utiliza actualmente por defecto en Windows con Direct3D 11 y con OpenGL cuando se utiliza opengl32.dll, Linux excluyendo Mesa llvmpipe, macOS con Metal, plataformas móviles y Embedded Linux con EGLFS, y con Vulkan independientemente de la plataforma. Todo esto puede cambiar en futuras versiones. Siempre es posible forzar el uso del renderizador por hilos configurando QSG_RENDER_LOOP=threaded en el entorno.

Bucle de renderizado sin subprocesos ('básico')

El bucle de renderizado no roscado se utiliza actualmente por defecto en Windows con OpenGL cuando no se utiliza el opengl32.dll estándar del sistema, macOS con OpenGL, WebAssembly y Linux con algunos controladores. En este último caso, se trata sobre todo de una medida de precaución, ya que no se han probado todas las combinaciones de controladores OpenGL y sistemas de ventanas.

En macOS y OpenGL, el bucle de renderizado enhebrado no es compatible cuando se compila con XCode 10 (10.14 SDK) o posterior, ya que esto opta por vistas por capas en macOS 10.14. Puede compilar con Xcode 9 (10.13 SDK) para optar por no utilizar el bucle de renderizado por capas, en cuyo caso el bucle de renderizado por hilos está disponible y se utiliza por defecto. No existe tal restricción con Metal.

El bucle de renderizado con subprocesos no es compatible con WebAssembly, ya que la plataforma web tiene un soporte limitado para el uso de WebGL en subprocesos distintos del principal, y un soporte limitado para el bloqueo del subproceso principal.

Incluso cuando se utiliza el bucle de renderizado sin subprocesos, debe escribir el código como si estuviera utilizando el renderizador con subprocesos, ya que no hacerlo hará que el código no sea transportable.

La siguiente es una ilustración simplificada de la secuencia de renderizado de fotogramas en el renderizador sin hilos.

Animaciones de conducción

¿A qué se refiere Advance Animations en los diagramas anteriores?

Por defecto, una animación Qt Quick (como, por ejemplo, NumberAnimation) es conducida por el controlador de animación por defecto. Esto se basa en temporizadores básicos del sistema, como QObject::startTimer(). El temporizador se ejecuta típicamente con un intervalo de 16 milisegundos. Aunque esto nunca será totalmente preciso y también depende de la precisión de los temporizadores en la plataforma subyacente, tiene la ventaja de ser independiente del renderizado. Proporciona resultados uniformes independientemente de la frecuencia de actualización de la pantalla y de si la sincronización con la sincronización vertical de la pantalla está activa o no. Así es como funcionan las animaciones con el bucle de renderizado basic.

Con el fin de proporcionar resultados más precisos con menos tartamudeo en la pantalla, independientemente del diseño del bucle de renderizado (ya sea de un solo hilo o de múltiples hilos) un bucle de renderizado puede decidir instalar su propio controlador de animación personalizado, y tomar la operación de advancing en sus propias manos, sin depender de temporizadores.

Esto es lo que implementa el bucle de renderizado threaded. De hecho, no instala uno, sino dos controladores de animación: uno en el hilo gui (para controlar las animaciones normales, como NumberAnimation), y otro en el hilo de renderizado (para controlar las animaciones del hilo de renderizado, es decir, los tipos de Animator, como OpacityAnimator o XAnimator). Ambas se adelantan durante la preparación de un fotograma, es decir, las animaciones están ahora sincronizadas con el renderizado. Esto tiene sentido debido a que la presentación está acelerada a la sincronización vertical de la pantalla por la pila de gráficos subyacente.

Por lo tanto, en el diagrama del bucle de renderizado threaded anterior, hay un paso explícito Advance animations en ambos subprocesos. Para el hilo de renderizado, esto es trivial: como el hilo está siendo acelerado a vsync, avanzar las animaciones (para los tipos Animator ) en cada fotograma como si hubieran transcurrido 16,67 milisegundos da resultados más precisos que confiar en un temporizador del sistema. (cuando se ajusta a la sincronización vsync, que es de 1000/60 milisegundos con una frecuencia de actualización de 60 Hz, es razonable suponer que ha transcurrido aproximadamente ese tiempo desde que se realizó la misma operación en el fotograma anterior).

El mismo enfoque funciona para las animaciones en el hilo de la interfaz gráfica (principal) también: debido a la sincronización esencial de los datos entre los hilos de la interfaz gráfica y de renderizado, el hilo de la interfaz gráfica es efectivamente acelerado a la misma velocidad que el hilo de renderizado, mientras que todavía tiene la ventaja de tener menos trabajo que hacer, dejando más espacio para la lógica de la aplicación, ya que gran parte de los preparativos de renderizado están ahora descargados en el hilo de renderizado.

Aunque en los ejemplos anteriores se han utilizado 60 imágenes por segundo, Qt Quick también está preparado para otras frecuencias de actualización: la frecuencia se consulta a QScreen y a la plataforma. Por ejemplo, con una pantalla de 144 Hz el intervalo es de 6,94 ms. Al mismo tiempo, esto es exactamente lo que puede causar problemas si el estrangulamiento basado en vsync no está funcionando como se espera, porque si lo que el bucle de renderizado cree que está sucediendo no coincide con la realidad, se producirá un ritmo de animación incorrecto.

Nota: A partir de Qt 6.5, el bucle de renderizado con hilos ofrece la posibilidad de optar por otro controlador de animación, basándose únicamente en el tiempo transcurrido (QElapsedTimer). Para habilitar esto, establezca la variable de entorno QSG_USE_SIMPLE_ANIMATION_DRIVER a un valor distinto de cero. Esto tiene las ventajas de no necesitar ninguna de las infraestructuras para volver a QTimer cuando hay múltiples ventanas, no necesitar heurística para determinar si falta o está rota la aceleración basada en vsync, ser compatible con cualquier tipo de desviaciones temporales en la aceleración vsync, y no estar ligado a la frecuencia de refresco de la pantalla principal, por lo que potencialmente funciona mejor en configuraciones multipantalla. También maneja correctamente las animaciones de los hilos de renderizado (los tipos de Animator ) incluso si la ralentización basada en vsync está rota o desactivada. Por otro lado, las animaciones pueden percibirse como menos fluidas con este enfoque. Teniendo en cuenta la compatibilidad, por el momento se ofrece como una función opcional.

En resumen, se espera que el bucle de renderizado threaded proporcione animaciones más fluidas con menos tartamudeo siempre que se cumplan las siguientes condiciones:

  • Hay exactamente una ventana (como en QQuickWindow) en pantalla.
  • La ralentización basada en VSync funciona como es de esperar con la pila de gráficos y pantallas underyling.

¿Qué ocurre si no hay ninguna ventana visible o hay más de una?

Cuando no hay ninguna ventana renderizable, por ejemplo porque nuestro QQuickWindow está minimizado (Windows) o totalmente oscurecido (macOS), no podemos presentar fotogramas, por lo que no podemos confiar en que el hilo "trabaje" al mismo ritmo que la frecuencia de refresco de la pantalla. En este caso, el bucle de renderizado threaded cambia automáticamente a un enfoque basado en el temporizador del sistema para controlar las animaciones, es decir, cambia temporalmente al mecanismo que utilizaría el bucle basic.

Lo mismo ocurre cuando hay más de una instancia de QQuickWindow en pantalla. El modelo presentado anteriormente para avanzar animaciones en el hilo gui, habilitado por su sincronización con el hilo render, ya no es satisfactorio, ya que ahora hay múltiples puntos de sincronización con múltiples hilos render. (uno por ventana.) Aquí también se hace necesario volver al enfoque basado en el temporizador del sistema, porque el tiempo y la frecuencia con la que el subproceso gui se bloqueará depende ahora de una serie de factores, incluyendo el contenido de las ventanas (¿se están animando? ¿con qué frecuencia se están actualizando?) y el comportamiento de la pila de gráficos (¿cómo maneja exactamente dos o más subprocesos que se presentan con wait-for-vsync?). Dado que no podemos garantizar que se ajuste a la velocidad de presentación de la ventana (¿qué ventana sería esa, para empezar?) de forma estable y multiplataforma, el avance de las animaciones no puede basarse en el renderizado.

Este cambio de mecanismos de gestión de las animaciones es transparente para las aplicaciones.

¿Y si el estrangulamiento basado en vsync es disfuncional, se desactiva globalmente o la propia aplicación lo desactiva?

El bucle de renderizado threaded depende de la implementación de la API gráfica y/o del sistema de ventanas para la aceleración, por ejemplo, solicitando un intervalo de intercambio de 1 en el caso de OpenGL (GLX, EGL, WGL), llamando a Present() con un intervalo de 1 para Direct 3D, o utilizando el modo de presentación FIFO con Vulkan.

Algunos controladores gráficos permiten a los usuarios anular esta configuración y desactivarla, ignorando la petición de Qt. Un ejemplo de esto sería un panel de control de todo el sistema del controlador gráfico que permite anular la configuración de la aplicación con respecto a vsync. También puede ocurrir que una pila de gráficos sea incapaz de proporcionar un estrangulamiento basado en vsync adecuado, lo que puede ser el caso en algunas máquinas virtuales (principalmente debido al uso de una implementación de OpenGL o Vulkan basada en rasterización por software).

Sin bloquear la operación de intercambio/presentación (o alguna otra operación gráfica), un bucle de renderizado de este tipo haría avanzar las animaciones demasiado rápido. Esto no sería un problema con el bucle de renderizado de basic, porque siempre depende de los temporizadores del sistema. Con threaded, el comportamiento puede variar en función de la versión de Qt:

  • Si se sabe que un sistema es incapaz de proporcionar una ralentización basada en vsync, la única opción antes de Qt 6.4 era usar el bucle de renderizado basic, configurando manualmente QSG_RENDER_LOOP=basic en el entorno antes de ejecutar la aplicación.
  • A partir de Qt 6.4, establecer la variable de entorno QSG_NO_VSYNC a un valor distinto de cero, o la ventana QSurfaceFormat::swapInterval() a 0 puede aliviar el problema también: al solicitar explícitamente la desactivación del bloqueo basado en vsync, independientemente de que la solicitud tenga algún efecto en la práctica, el bucle de renderizado threaded puede, por extensión, reconocer que confiar en vsync para impulsar las animaciones es inútil, y volverá a utilizar los temporizadores del sistema, tal como lo haría para más de una ventana.
  • Incluso mejor, a partir de Qt 6.4 el scenegraph también intenta reconocer usando algunas heurísticas simples que los fotogramas se están presentando "demasiado rápido", y cambia automáticamente a los temporizadores del sistema si lo considera necesario. Esto significa que en la mayoría de los casos no habrá necesidad de hacer nada y las aplicaciones ejecutarán las animaciones como se espera incluso cuando el bucle de renderizado por defecto sea el de threaded. Aunque esto es transparente para las aplicaciones, a efectos de resolución de problemas y desarrollo es útil saber que esto se registra con un mensaje "Window 0x7ffc8489c3d0 is determined to have broken vsync throttling ..." impreso cuando se activa QSG_INFO o qt.scenegraph.general. Este método tiene el inconveniente de que sólo se activa después de un pequeño conjunto de fotogramas, dado que primero necesita recopilar datos para evaluar, lo que significa que al abrir un QQuickWindow la aplicación puede seguir mostrando animaciones demasiado rápidas durante un breve periodo de tiempo. Además, puede que no capture todas las posibles situaciones de vsync-broken.

Recuerda, sin embargo, que por diseño nada de esto ayuda a renderizar animaciones de hilos (los tipos de Animator ). En ausencia de bloqueo basado en vsync, animators avanzará incorrectamente por defecto, más rápido de lo esperado, incluso cuando se activen las soluciones para animations normales. Si esto se convierte en un problema, considere utilizar el controlador de animación alternativo configurando QSG_USE_SIMPLE_ANIMATION_DRIVER.

Nota: Tenga en cuenta que la lógica del bucle de renderizado y el procesamiento de eventos en el hilo GUI (principal) no es necesariamente unthrottled incluso si la espera de vsync está desactivada: ambos bucles de renderizado programan actualizaciones para ventanas a través de QWindow::requestUpdate(). Esto está respaldado por un temporizador de hilo GUI de 5 ms en la mayoría de las plataformas, con el fin de dar tiempo para el procesamiento de eventos. En algunas plataformas, como macOS, utiliza APIs específicas de la plataforma (como CVDisplayLink) para recibir notificaciones sobre el momento adecuado para preparar un nuevo fotograma, probablemente vinculado de alguna forma al vsync de la pantalla. Esto puede ser relevante en benchmarking y situaciones similares. Para aplicaciones y herramientas que intenten realizar benchmarking de bajo nivel puede ser beneficioso establecer la variable de entorno QT_QPA_UPDATE_IDLE_TIME a 0 para reducir potencialmente el tiempo de inactividad en el hilo GUI. Para el uso normal de la aplicación los valores por defecto deberían, en la mayoría de los casos, ser suficientes.

Nota: En caso de duda, active las categorías de registro qt.scenegraph.general y qt.scenegraph.time.renderloop para la solución de problemas, ya que pueden revelar algunas pistas sobre por qué el renderizado y las animaciones no se están ejecutando al ritmo esperado.

Control personalizado sobre el renderizado con QQuickRenderControl

Cuando se utiliza QQuickRenderControl, la responsabilidad de controlar el bucle de renderizado se transfiere a la aplicación. En este caso no se utiliza ningún bucle de renderizado integrado. En su lugar, depende de la aplicación invocar los pasos de pulido, sincronización y renderizado en el momento adecuado. Es posible implementar un comportamiento roscado o no roscado similar a los mostrados anteriormente.

Además, las aplicaciones pueden desear implementar e instalar su propio QAnimationDriver en combinación con QQuickRenderControl. Esto da un control total sobre la conducción de animaciones Qt Quick, que puede ser particularmente importante para el contenido que no se muestra en pantalla, sin relación con la tasa de presentación, simplemente porque no hay presentación del marco sucediendo. Esto es opcional, por defecto las animaciones avanzarán basadas en el temporizador del sistema.

Ampliación del gráfico de escena con renderizado nativo 3D y basado en QRhi

El gráfico de escena ofrece tres métodos para integrar comandos gráficos proporcionados por la aplicación:

  • Emisión de comandos basados en QRhi u OpenGL, Vulkan, Metal, Direct3D directamente antes o después de la propia renderización del gráfico de escena. En efecto, esto anexa o añade un conjunto de llamadas de dibujo al pase de renderizado principal. No se utiliza ningún objetivo de renderizado adicional.
  • Renderizado a una textura y creación de un nodo texturizado en el gráfico de escena. Esto implica un pase de renderizado y un objetivo de renderizado adicionales.
  • Emisión de llamadas de dibujo en línea con el propio renderizado del gráfico de escena mediante la instanciación de una subclase QSGRenderNode en el gráfico de escena. Esto es similar al primer enfoque, pero las llamadas de dibujo personalizadas se inyectan en el flujo de comandos del gráfico de escena.

Modo subyacente/superpuesto

Al conectarse a las señales QQuickWindow::beforeRendering() y QQuickWindow::afterRendering(), las aplicaciones pueden realizar llamadas a QRhi o a la API 3D nativa directamente en el mismo contexto en el que se está renderizando el gráfico de escena. Con API como Vulkan o Metal, las aplicaciones pueden consultar objetos nativos, como el búfer de comandos del gráfico de escena, a través de QSGRendererInterface, y grabar en él los comandos que consideren oportunos. Como indican los nombres de las señales, el usuario puede entonces renderizar contenidos bajo una escena Qt Quick o sobre ella. La ventaja de esta integración es que no se necesitan objetivos de renderizado adicionales para realizar el renderizado, y se elimina un paso de texturizado posiblemente costoso. La desventaja es que el renderizado personalizado sólo puede realizarse al principio o al final del propio renderizado de Qt Quick. El uso de QSGRenderNode en lugar de las señales de QQuickWindow puede eliminar esta restricción, pero en cualquier caso hay que tener cuidado con el contenido 3D y el uso del buffer de profundidad, ya que confiar en las pruebas de profundidad y renderizar con la escritura de profundidad activada puede crear fácilmente situaciones en las que el contenido personalizado y el uso del buffer de profundidad del contenido de Qt Quick entren en conflicto.

A partir de Qt 6.6, las API de QRhi se consideran semipúblicas, es decir, se ofrecen a las aplicaciones y están documentadas, aunque con una garantía de compatibilidad limitada. Esto permite crear código de renderizado 2D/3D portable y multiplataforma utilizando las mismas abstracciones gráficas y de sombreado que utiliza el propio gráfico de escena.

El ejemplo Scene Graph - RHI Under QML da un ejemplo de cómo implementar el enfoque underlay/overlay usando QRhi.

El ejemplo Scene Graph - OpenGL Under QML muestra cómo utilizar estas señales con OpenGL.

El ejemplo Scene Graph - Direct3D 11 Under QML muestra cómo utilizar estas señales con Direct3D.

La Gráfica de Escena - Metal Bajo QML da un ejemplo de cómo usar estas señales usando Metal.

El ejemplo Scene Graph - Vulkan Under QML da un ejemplo de cómo usar estas señales usando Vulkan.

A partir de Qt 6.0, el uso directo de la API gráfica subyacente debe ir acompañado de una llamada a QQuickWindow::beginExternalCommands() y QQuickWindow::endExternalCommands(). Este concepto puede ser familiar de QPainter::beginNativePainting(), y sirve un propósito similar: permite al Qt Quick Scene Graph reconocer que cualquier estado en caché y suposiciones sobre el estado dentro del pase de renderizado actualmente grabado, si hay uno, son ahora inválidos, porque el código de la aplicación puede haberlo alterado trabajando directamente con la API gráfica subyacente. Esto no es aplicable y necesario cuando se utiliza QRhi.

Cuando se mezcla el renderizado OpenGL personalizado con el gráfico de escena, es importante que la aplicación no deje el contexto OpenGL en un estado con buffers vinculados, atributos habilitados, valores especiales en el z-buffer o stencil-buffer o similares. Hacerlo puede resultar en un comportamiento impredecible.

El código de renderizado personalizado debe ser consciente de los hilos en el sentido de que no debe asumir que se ejecuta en el hilo GUI (principal) de la aplicación. Al conectarse a las señales de QQuickWindow, la aplicación debe utilizar Qt::DirectConnection y entender que las ranuras conectadas se invocan en el hilo de renderizado dedicado del gráfico de escena, si existe.

El enfoque basado en texturas

La alternativa basada en texturas es el enfoque más flexible cuando la aplicación necesita tener una imagen 2D "aplanada" de algún renderizado 3D personalizado dentro de la escena Qt Quick. También permite utilizar un búfer de profundidad/esténcil dedicado que es independiente de los búferes utilizados por el pase de renderizado principal.

Cuando se utiliza OpenGL, la clase de conveniencia legado QQuickFramebufferObject se puede utilizar para lograr esto. QRhi Los renderizadores personalizados y las API de gráficos que no sean OpenGL también pueden seguir este enfoque, aunque QQuickFramebufferObject no los admita actualmente. La creación y renderización de una textura directamente con la API subyacente, seguida de la envoltura y uso de este recurso en una escena Qt Quick en un QQuickItem personalizado, se demuestra en los siguientes ejemplos:

Gráfico de escena - Ejemplo deelemento de textura RHI.

Gráfico de escena: ejemplo deimportación de textura Vulkan.

Gráfico de escena: ejemplo deimportación de textura metálica.

El enfoque en línea

Usando QSGRenderNode las llamadas de dibujo personalizadas se inyectan no al principio o al final de la grabación del pase de renderizado del gráfico de escena, sino durante el proceso de renderizado del gráfico de escena. Esto se consigue creando un QQuickItem personalizado basado en una instancia de QSGRenderNode, un nodo del gráfico de escena que existe específicamente para permitir la emisión de comandos gráficos a través de QRhi o de una API 3D nativa como OpenGL, Vulkan, Metal o Direct 3D.

El ejemplo Scene Graph - Custom QSGRenderNode ofrece una demostración de este enfoque.

Elementos personalizados usando QPainter

El QQuickItem proporciona una subclase, QQuickPaintedItem, que permite a los usuarios renderizar contenido usando QPainter.

Advertencia: El uso de QQuickPaintedItem utiliza una superficie 2D indirecta para renderizar su contenido, ya sea utilizando rasterización por software o utilizando un objeto framebuffer OpenGL (FBO), por lo que el renderizado es una operación de dos pasos. Primero se rasteriza la superficie y luego se dibuja. Utilizar directamente la API de gráficos de escena es siempre mucho más rápido.

Soporte de registro

El gráfico de escena admite varias categorías de registro. Estos pueden ser útiles en el seguimiento de los problemas de rendimiento y errores, además de ser útil para los colaboradores de Qt.

  • qt.scenegraph.time.texture - registra el tiempo empleado en cargar texturas
  • qt.scenegraph.time.compilation - registra el tiempo empleado en la compilación de shaders
  • qt.scenegraph.time.renderer - registra el tiempo empleado en los distintos pasos del renderizador
  • qt.scenegraph.time.renderloop - registra el tiempo empleado en los distintos pasos del bucle de renderizado. Con el bucle de renderizado threaded, esto nos da una idea del tiempo transcurrido entre los distintos pasos de preparación de fotogramas, tanto en la GUI como en el hilo de renderizado. Por lo tanto, también puede ser una herramienta útil de solución de problemas, por ejemplo, para confirmar cómo vsync basado en la aceleración y otros de bajo nivel Qt habilitadores, como QWindow::requestUpdate(), afectar a la representación y presentación de tuberías.
  • qt.scenegraph.time.glyph - registra el tiempo empleado en preparar los glifos del campo de distancia
  • qt.scenegraph.general - registra información general sobre diversas partes del gráfico de escena y la pila de gráficos
  • qt.scenegraph.renderloop - crea un registro detallado de las distintas etapas de la renderización. Este modo de registro es útil sobre todo para los desarrolladores que trabajan con Qt.

La variable de entorno QSG_INFO también está disponible. Si se establece en un valor distinto de cero, se activa la categoría qt.scenegraph.general.

Nota: Cuando se encuentre con problemas gráficos, o en caso de duda sobre qué bucle de renderizado o API gráfica se está utilizando, inicie siempre la aplicación con al menos qt.scenegraph.general y qt.rhi.* habilitados, o QSG_INFO=1 configurado. Esto imprimirá alguna información esencial en la salida de depuración durante la inicialización.

Backend de Scene Graph

Además de la API pública, el gráfico de escena tiene una capa de adaptación que abre la implementación para hacer adaptaciones específicas de hardware. Se trata de un plugin API interno y privado, no documentado, que permite a los equipos de adaptación sacar el máximo partido de su hardware. Incluye:

  • Texturas personalizadas; específicamente la implementación de QQuickWindow::createTextureFromImage y la representación interna de la textura utilizada por los tipos Image y BorderImage.
  • Renderizador personalizado; la capa de adaptación permite al complemento decidir cómo se recorre y renderiza el gráfico de escena, lo que hace posible optimizar el algoritmo de renderizado para un hardware específico o hacer uso de extensiones que mejoran el rendimiento.
  • Implementación personalizada del gráfico de escena de muchos de los tipos QML predeterminados, incluida la representación de texto y fuentes.
  • Controlador de animación personalizado; permite que el sistema de animación se enganche al refresco vertical de bajo nivel de la pantalla para obtener un renderizado fluido.
  • Bucle de renderizado personalizado; permite un mejor control sobre cómo QML trata las ventanas múltiples.

© 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.