Qt para Linux embebido
Plugins de plataforma para dispositivos Linux embebidos
En sistemas Linux embebidos, existen múltiples plugins de plataforma que puedes utilizar: EGLFS, VkKhrDisplay, LinuxFB, o Wayland. La disponibilidad de estos plugins depende de cómo esté configurado Qt. De estos, Wayland requiere la presencia de un compositor, y proporciona un sistema de ventanas completo que soporta múltiples ventanas, de forma similar a X11 o Windows. Los otros funcionan sin ningún sistema de ventanas, lo que significa que la aplicación Qt tiene el control total del renderizado y la salida. Normalmente admiten una "ventana" Qt a pantalla completa por pantalla.
EGLFS es el plugin por defecto en muchas placas. Si no es adecuado, utiliza la variable de entorno QT_QPA_PLATFORM para solicitar otro plugin. Alternativamente, para pruebas rápidas, utilice el argumento de línea de comandos -platform con la misma sintaxis.
Nota: A partir de Qt 5.0, Qt ya no tiene su propia implementación del sistema de ventanas (QWS). Para casos de uso de un solo proceso, la Abstracción de Plataforma Qt es una solución superior; los casos de uso multiproceso son soportados a través de Wayland.
Ver Configurar un Dispositivo Linux Embebido para una visión general de la configuración de Qt para la compilación cruzada utilizando una cadena de herramientas Linux Embebido.
EGLFS
EGL es una interfaz entre OpenGL y el sistema de ventanas nativo. Qt puede utilizar EGL para la gestión de contextos y superficies, sin embargo la API no contiene ninguna plataforma específica. La creación de una ventana nativa, que no será necesariamente una ventana real en la pantalla, debe hacerse por medios específicos de la plataforma. Por eso necesitamos el código de adaptación específico de la placa o la GPU. Típicamente, estas adaptaciones se proporcionan como:
- EGLFS hooks - un único archivo fuente compilado en el plugin de la plataforma
- Integración de dispositivos EGL - plugins cargados dinámicamente
EGLFS es un plugin de plataforma para ejecutar aplicaciones Qt sobre EGL y OpenGL ES 2.0, sin un sistema de ventanas como X11 o Wayland. Es el plugin recomendado para dispositivos Linux embebidos modernos que incluyen una GPU.
Además de Qt Quick y las aplicaciones OpenGL nativas, EGLFS soporta ventanas renderizadas por software, como QWidget, también. En el caso de QWidget, el contenido de los widgets se renderiza utilizando la CPU en imágenes, que luego se cargan en texturas y son compuestas por el plugin.
EGLFS fuerza a la primera ventana de nivel superior, ya sea QWidget o QQuickView, a convertirse en pantalla completa. Esta ventana también se elige para ser la ventana raíz del widget en la que se componen todos los demás widgets de nivel superior. Por ejemplo, cuadros de diálogo, menús emergentes o cuadros combinados. Este comportamiento es necesario porque con EGLFS siempre hay exactamente una ventana nativa y una superficie de ventana EGL; éstas pertenecen al widget o ventana que se crea primero. Este enfoque funciona bien cuando hay una ventana principal que existe durante toda la vida de la aplicación y todos los demás widgets no son de nivel superior o se crean después, una vez que se muestra la ventana principal.
Existen más restricciones para las ventanas basadas en OpenGL. EGLFS soporta una única ventana GL a pantalla completa (a partir de Qt 5.3), como las basadas en OpenGL QWindow, a QQuickView, o a QOpenGLWidget. Abrir ventanas OpenGL adicionales o mezclar dichas ventanas con contenido basado en QWidget no está soportado; Qt termina la aplicación con un mensaje de error.
Además, las APIs diseñadas para plataformas de escritorio o entornos con un sistema de ventanas, como Drag and Drop, no están soportadas en EGLFS.
Variables de entorno utilizadas por EGLFS
Si es necesario, eglfs puede configurarse utilizando las siguientes variables de entorno:
| Variable de entorno | Descripción |
|---|---|
QT_QPA_EGLFS_INTEGRATION | Además de los hooks compilados, también es posible utilizar plugins cargados dinámicamente para proporcionar una adaptación específica del dispositivo o del proveedor. Esta variable de entorno impone un plugin específico. Por ejemplo, si se establece como eglfs_kms, se utiliza el backend KMS/DRM. Esto sólo es una opción cuando no se han especificado ganchos estáticos o compilados en las especificaciones del dispositivo. En la práctica, los hooks compilados tradicionales se usan raramente, casi todos los backends han migrado a plugins. Los makespecs de dispositivo todavía contienen una entrada relevante, aunque opcional, EGLFS_DEVICE_INTEGRATION: el nombre del backend preferido para ese dispositivo en particular. Evite configurar esta variable de entorno si hay más de un plugin presente en el sistema de destino. En un entorno de escritorio, los backends KMS o X11 tienen prioridad, dependiendo de la presencia de la variable de entorno DISPLAY.Nota: En algunas placas se utiliza un valor especial de |
QT_QPA_EGLFS_PHYSICAL_WIDTH y QT_QPA_EGLFS_PHYSICAL_HEIGHT | Especifica la anchura y altura de la pantalla física en milímetros. Ten en cuenta que desde Qt 6 el tamaño físico de la pantalla ya no se usa para determinar los ppp lógicos. |
QT_QPA_EGLFS_ROTATION | Especifica la rotación aplicada al contenido renderizado por software en aplicaciones basadas en QWidget. Los valores admitidos son 180, 90 y -90. Esta variable no se aplica a las ventanas basadas en OpenGL, incluida Qt Quick. En cambio, las aplicaciones Qt Quick pueden aplicar transformaciones en su escena QML. El cursor del ratón estándar de eglfs siempre tiene en cuenta el valor, con una imagen del puntero adecuadamente posicionada y girada, independientemente del tipo de aplicación. Sin embargo, es posible que las implementaciones especiales del cursor, como el cursor de hardware del backend KMS/DRM, no admitan la rotación. Esta configuración no afecta a nada más, incluida la entrada táctil. Los backends de entrada táctil evdevtouch y libinput tienen cada uno sus propios mecanismos para configurar la rotación. Consulte Entradas en un dispositivo Linux integrado para obtener más información sobre la configuración de la entrada táctil. |
QT_QPA_EGLFS_FORCEVSYNC | Cuando está activada, eglfs solicita FBIO_WAITFORVSYNC en el dispositivo framebuffer después de cada llamada a eglSwapBuffers(). Esta variable sólo es relevante para los backends que dependen del subsistema Linux fbdev. Normalmente, con un intervalo de intercambio predeterminado de 1, Qt asume que la llamada a eglSwapBuffers() se encarga de vsync; si no lo hace (por ejemplo, debido a errores del controlador), intenta establecer QT_QPA_EGLFS_FORCEVSYNC a un valor distinto de cero. |
QT_QPA_EGLFS_FORCE888 | Cuando se establece, los tamaños de los canales de color rojo, verde y azul se ignoran cuando eglfs crea un nuevo contexto, ventana o superficie fuera de pantalla. En su lugar, el complemento solicita una configuración con 8 bits por canal. Esto puede ser útil en dispositivos en los que se eligen por defecto configuraciones con menos de 32 o 24 bits por píxel (por ejemplo, 5-6-5 o 4-4-4) a pesar de saber que no son ideales, por ejemplo, debido a los efectos de banding. En lugar de cambiar el código de la aplicación, esta variable proporciona un atajo para forzar configuraciones de 24 o 32 bpp. |
Además, están disponibles las siguientes variables de uso menos frecuente:
| Variable de entorno | Descripción |
|---|---|
QT_QPA_EGLFS_FB | Anula el dispositivo framebuffer. Por defecto es /dev/fb0. En la mayoría de las plataformas embebidas, esta variable no es muy relevante porque el framebuffer se utiliza sólo para consultar ajustes como las dimensiones de la pantalla. Sin embargo, en ciertos dispositivos, esta variable proporciona la capacidad de especificar qué pantalla utilizar en múltiples configuraciones de pantalla, similar al parámetro fb en LinuxFB. |
QT_QPA_EGLFS_WIDTH y QT_QPA_EGLFS_HEIGHT | Contiene la anchura y la altura de la pantalla en píxeles. Aunque eglfs intenta determinar las dimensiones desde el dispositivo framebuffer /dev/fb0, esto no siempre funciona. Puede ser necesario especificar manualmente los tamaños. |
QT_QPA_EGLFS_DEPTH | Anula la profundidad de color de la pantalla. En plataformas en las que el dispositivo framebuffer /dev/fb0 no está disponible o la consulta no tiene éxito, se utiliza por defecto 32. Utilice esta variable para anular estos valores por defecto.Nota: Esta variable sólo afecta al valor de profundidad de color indicado por QScreen. No tiene relación con las configuraciones EGL ni con la profundidad de color utilizada para el renderizado OpenGL. |
QT_QPA_EGLFS_SWAPINTERVAL | Por defecto, se solicita un intervalo de intercambio de 1. Esta variable permite la sincronización con el refresco vertical de la pantalla. Utilice esta variable para anular el valor del intervalo de intercambio. Por ejemplo, si se pasa 0 se desactiva el bloqueo en el intercambio, lo que resulta en una ejecución lo más rápida posible sin ninguna sincronización. |
QT_QPA_EGLFS_DEBUG | Cuando se establece, se imprime alguna información de depuración en la salida de depuración. Por ejemplo, la entrada QSurfaceFormat y las propiedades de la configuración EGL elegida se imprimen al crear un nuevo contexto. Cuando se utiliza junto con Qt Quick's QSG_INFO variable, puede obtener información útil para la solución de problemas relacionados con la configuración de EGL. |
Registro
Además de QT_QPA_EGLFS_DEBUG, eglfs también soporta el moderno sistema de registro categorizado de Qt. Están disponibles las siguientes categorías de registro:
qt.qpa.egldeviceintegration- Activa el registro para los backends cargados dinámicamente. Utilice esta categoría para comprobar qué backend está en uso.qt.qpa.input- Activa la salida de depuración de los gestores de entradaevdevylibinput. Utilice esta categoría para comprobar si se ha reconocido y abierto un dispositivo de entrada determinado.qt.qpa.eglfs.kms- Activa el registro detallado en el backend KMS/DRM.
Después de ejecutar configure, asegúrese de inspeccionar su salida. Esta es la forma más fácil y rápida de identificar si tienes habilitado el backend EGLFS, libudev o libinput necesarios. En resumen, si hay un "no" no deseado en su configure salida, ejecute:
./configure -v
para activar la salida detallada, de modo que puedas ver las invocaciones del compilador y del enlazador para cada prueba de configure.
Nota: Si encuentras errores sobre cabeceras perdidas, librerías, o fallos aparentemente crípticos del enlazador, a menudo, son un signo de un sysroot incompleto o roto y no está relacionado con Qt.
Como ejemplo, cuando se apunta a la Raspberry Pi con los drivers gráficos propietarios de Broadcom, la salida debería contener algo como lo siguiente:
QPA backends: EGLFS ................................ yes EGLFS details: EGLFS i.Mx6 ........................ no EGLFS i.Mx6 Wayland ................ no EGLFS EGLDevice .................... no EGLFS GBM .......................... no EGLFS Mali ......................... no EGLFS Raspberry Pi ................. yes EGL on X11 ......................... no
Si este no es el caso, no es aconsejable continuar con la compilación ya que los gráficos acelerados no serán funcionales sin el backend específico de Raspberry Pi, incluso si el resto de Qt compila correctamente.
VkKhrDisplay
Mientras que EGLFS sólo soporta OpenGL (ES), VkKhrDisplay es un plugin de plataforma experimental que soporta renderizado con la API Vulkan. Para enumerar las pantallas y configurar el renderizado, se basa en la familia de extensiones VK_KHR_display. Tenga en cuenta que no se da el caso de que una implementación de Vulkan dentro de una pila de gráficos soporte esta funcionalidad. Actualmente este plugin de plataforma ha sido verificado y probado con Mesa y V3DV corriendo en una Raspberry Pi 4.
Este plugin de plataforma no soporta OpenGL ni ningún tipo de renderizado por software. Intentar mostrar una interfaz de usuario basada en QWidget fallará. El único tipo de superficie QWindow soportado es QSurface::VulkanSurface. Para aplicaciones Qt Quick esto implica que el renderizado basado en Vulkan debe ser forzado ya sea configurando QSG_RHI_BACKEND=vulkan en el entorno, o llamando a QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan); al principio antes de crear un QQuickWindow o QQuickView.
Para utilizar este plugin de plataforma, ejecute la aplicación con -platform vkkhrdisplay o establezca QT_QPA_PLATFORM a vkkhrdisplay. El plugin se construye sólo cuando Qt está configurado con soporte Vulkan.
La configuración avanzada al estilo EGLFS (por ejemplo, el archivo de configuración JSON) o la salida a múltiples pantallas desde la misma aplicación no están implementadas actualmente. Sin embargo, las aplicaciones pueden elegir la pantalla a utilizar a través de variables de entorno.
Para determinar los valores de índice, compruebe los registros impresos en la salida de depuración por el plugin. Actualmente estos registros no están categorizados (se imprimen a través de qDebug) ya que inspeccionarlos es esencial en la mayoría de los casos, para asegurar que el plugin elige la pantalla y el modo apropiados.
QT_VK_DISPLAY_INDEX- Cuando se establece, se utiliza la pantalla con el índice dado.QT_VK_MODE_INDEX- Cuando se establece, se utiliza el modo con el índice dado.QT_VK_PHYSICAL_DEVICE_INDEX- Cuando se establece, se utiliza el dispositivo físico Vulkan con el índice dado. Esto no será relevante en la mayoría de los casos. Tenga en cuenta que esta variable también es utilizada por el resto de la pila de gráficos Qt.
El manejo de la entrada (teclado, ratón, táctil) es similar a EGLFS, soportando evdev, libinput, y tslib. Sin embargo, no se implementa el renderizado del cursor del ratón. Esto se debe a que no existe el concepto de un cursor de hardware en este entorno, y la representación de un cursor con Vulkan dentro del plugin de plataforma, de manera similar a lo que EGLFS hace con OpenGL, es problemático por múltiples razones. Por lo tanto, este complemento de plataforma no es adecuado para la entrada basada en el ratón por el momento.
Las variables de entorno relacionadas son:
QT_QPA_DISABLE_INPUT- Desactiva la entrada de teclado/ratón/táctil.QT_QPA_NO_LIBINPUT- Prefiere los manejadores de entrada basados en evdev incluso cuando libinput está disponible.QT_QPA_TSLIB- Solicita el uso de la biblioteca tslib heredada.
LinuxFB
Este plugin escribe directamente en el framebuffer a través del subsistema fbdev de Linux. Sólo soporta contenido renderizado por software. Tenga en cuenta que, en algunas configuraciones, el rendimiento de la pantalla puede ser limitado. Para usar aplicaciones Qt Quick con este plugin de plataforma, se debe usar el backend software scenegraph, ya sea configurando QT_QUICK_BACKEND=software en el entorno, o llamando a setGraphicsApi() con QSGRendererInterface::Software. Se soportan aplicaciones QWidget, o QWindow con un tipo de superficie de QSurface::RasterSurface, pero esto no incluye widgets especiales como QOpenGLWidget.
Como fbdev está siendo obsoleto en el kernel de Linux, también está disponible el soporte de DRM dumb buffer. Para utilizarlo, establezca la variable de entorno QT_QPA_FB_DRM a un valor distinto de cero. Cuando se establece, siempre que los dumb buffers estén soportados por su sistema, no se accederá a los dispositivos framebuffer heredados como /dev/fb0. En su lugar, el renderizado se realiza a través de las APIs DRM, de forma similar al backend eglfs_kms en EGLFS. La salida es de doble búfer y de página volteada, proporcionando vsync adecuado para el contenido renderizado por software también.
Nota: Cuando se utilizan búferes tontos, ninguna de las opciones descritas a continuación es aplicable, ya que las propiedades como el tamaño físico y lógico de la pantalla se consultan automáticamente.
Especificación de ajustes adicionales
El complemento linuxfb permite especificar ajustes adicionales mediante la variable de entorno QT_QPA_PLATFORM o la opción de línea de comandos -platform. Por ejemplo, QT_QPA_PLATFORM=linuxfb:fb=/dev/fb1 especifica que se debe utilizar el dispositivo framebuffer /dev/fb1 en lugar del predeterminado fb0. Para especificar varias opciones, separe las m con dos puntos (:).
| Configuración | Descripción |
|---|---|
fb=/dev/fbN | Especifica los dispositivos framebuffer. En configuraciones de múltiples pantallas, esta configuración te permite ejecutar la aplicación en diferentes pantallas. Actualmente, no hay forma de usar múltiples framebuffers desde una aplicación Qt. |
size=<ancho>x<alto> | Especifica el tamaño de la pantalla en píxeles. El plugin intenta consultar las dimensiones de la pantalla, tanto físicas como lógicas, desde el dispositivo framebuffer. Sin embargo, esta consulta no siempre puede conducir a resultados adecuados; puede ser necesario especificar los valores explícitamente. |
mmsize=<anchura>x<altura> | Especifica la anchura y la altura físicas en milímetros. |
offset=<anchura>x<altura> | Especifica la esquina superior izquierda de la pantalla desplazada en píxeles. La posición por defecto es (0, 0). |
nographicsmodeswitch | Especifica que no se active el modo gráfico del terminal virtual (KD_GRAPHICS). Normalmente, al activar el modo gráfico se desactiva el cursor parpadeante y el borrado de pantalla. Sin embargo, cuando se establece este parámetro, esas dos características también se omiten. |
tty=/dev/ttyN | Anula la consola virtual. Sólo se utiliza cuando nographicsmodeswitch no está configurado. |
A partir de Qt 5.9, el comportamiento de EGLFS y LinuxFB se ha sincronizado, con respecto a la política de tamaño de las ventanas: la primera ventana de nivel superior se fuerza a cubrir toda la pantalla, con ambos plugins de plataforma. Si no se desea esto, establezca la variable de entorno QT_QPA_FB_FORCE_FULLSCREEN a 0 para restaurar el comportamiento de versiones anteriores de Qt.
Salida de pantalla
El nivel de soporte para apuntar a una o más pantallas desde una única aplicación Qt varía entre los plugins de plataforma. El soporte depende a menudo del dispositivo y su pila gráfica.
EGLFS con el backend eglfs_kms
Cuando el backend KMS/DRM está en uso, EGLFS informa de todas las pantallas disponibles en QGuiApplication::screens(). Las aplicaciones pueden apuntar a diferentes pantallas con diferentes ventanas a través de QWindow::setScreen().
Nota: Se sigue aplicando la restricción de una única ventana a pantalla completa por pantalla. Tampoco es posible cambiar de pantalla después de hacer visible QWindow. Por lo tanto, es esencial que las aplicaciones embebidas realicen todas las llamadas necesarias a QWindow::setScreen() antes de llamar a QWindow::show().
Cuando empiezas a desarrollar en un dispositivo embebido dado, a menudo es necesario verificar el comportamiento del dispositivo y los controladores, y que las pantallas conectadas funcionan como deberían. Una forma sencilla es utilizar el ejemplo hellowindow. Al ejecutarlo con los argumentos -platform eglfs --multiscreen --timeout se muestra un logotipo Qt giratorio en cada pantalla conectada durante unos segundos.
Configuración personalizada
El backend KMS/DRM también admite configuraciones personalizadas a través de un archivo JSON. Para habilitarlo, establezca la variable de entorno QT_QPA_EGLFS_KMS_CONFIG con el nombre del archivo. También puede incrustar este archivo en la aplicación a través del sistema de recursos Qt.
La mayoría de estas opciones de configuración se aplican a todos los backends basados en KMS/DRM, independientemente de la tecnología de gestión de búferes (GBM o EGLStreams).
Nota: Los archivos de configuración se consideran contenido de confianza totalmente controlado y gestionado por el creador del dispositivo o plataforma. No se espera que estén expuestos al usuario final de ninguna forma.
He aquí un ejemplo de configuración:
{
"device": "/dev/dri/card1",
"hwcursor": false,
"pbuffers": true,
"outputs": [
{
"name": "VGA1",
"mode": "off"
},
{
"name": "HDMI1",
"mode": "1024x768"
}
]
}Aquí configuramos el dispositivo especificado para que:
- No utilice el cursor por hardware (vuelve a renderizar el cursor del ratón a través de OpenGL; por defecto los cursores por hardware están habilitados ya que son más eficientes).
- Respalde QOffscreenSurface con superficies pbuffer EGL estándar (por defecto está desactivado y en su lugar se utiliza una superficie gbm).
- La salida en el conector VGA está desactivada, mientras que HDMI está activa con una resolución de 1024x768.
Además, esta configuración también desactiva la búsqueda de un dispositivo a través de libudev; en su lugar se utiliza el dispositivo especificado.
Cuando no se define mode, se elige el modo preferido del sistema. Los valores aceptados para mode son: off, current, preferred, skip, anchoxalto, anchoxalto@vrefresh, o una cadena modeline.
Especificando current se elige un modo con una resolución que coincide con la actual. Dado que el ajuste de modo sólo se realiza cuando el modo deseado es realmente diferente del activo (a menos que se fuerce mediante la variable de entorno QT_QPA_EGLFS_ALWAYS_SET_MODE ), este valor es útil para preservar el modo actual y cualquier contenido en los planos no tocados por Qt.
skip hace que el conector para la salida sea ignorado como si estuviera desconectado. off es similar, pero cambia el modo y apaga la pantalla.
Comportamiento por defecto
Por defecto, todas las pantallas reportadas por la capa DRM son tratadas como un gran escritorio virtual. La implementación del cursor del ratón tiene esto en cuenta y se mueve a través de las pantallas como se espera. Aunque no se recomienda, puede desactivar el escritorio virtual estableciendo separateScreens en false en la configuración.
Por defecto, el escritorio virtual se forma de izquierda a derecha, basándose en el orden de los conectores según informa el sistema. Para cambiar esto, configure virtualIndex con un valor que empiece por 0.
Por ejemplo, la siguiente configuración utiliza la resolución preferida pero asegura que el lado izquierdo del escritorio virtual sea la pantalla conectada al puerto HDMI; mientras que el lado derecho es la pantalla conectada al DisplayPort:
{
"device": "drm-nvdc",
"outputs": [
{
"name": "HDMI1",
"virtualIndex": 0
},
{
"name": "DP1",
"virtualIndex": 1
}
]
}El orden de los elementos en la matriz no es relevante. Las salidas con índices virtuales no especificados se colocan a continuación de las demás, conservando el orden original en la lista de conectores DRM.
Para crear un espacio de escritorio vertical (es decir, para apilar de arriba a abajo en lugar de de izquierda a derecha), añada una propiedad virtualDesktopLayout después de device con el valor de vertical.
Advertencia: Se recomienda que todas las pantallas del escritorio virtual utilicen la misma resolución, de lo contrario elementos como el cursor del ratón pueden comportarse de forma inesperada al entrar en zonas que sólo existen en una pantalla determinada.
Cuando virtualIndex no es suficiente, se puede utilizar la propiedad virtualPos para especificar explícitamente la posición superior izquierda de la pantalla en cuestión. Tomando el ejemplo anterior y asumiendo una resolución de 1080p para HDMI1, el siguiente fragmento de código coloca una segunda pantalla basada en HDMI debajo de la primera:
{
...
"outputs": [
...
{
"name": "HDMI2",
"virtualPos": "0, 1080"
}
]
}Nota: Evite este tipo de configuraciones cuando desee utilizar el ratón. El comportamiento del cursor del ratón puede ser inesperado con diseños no lineales. La pantalla táctil no debería presentar problemas.
Consulta automática del tamaño físico de la pantalla
En algunos casos, la consulta automática del tamaño físico de la pantalla a través de DRM puede fallar. Normalmente se utilizarían las variables de entorno QT_QPA_EGLFS_PHYSICAL_WIDTH y QT_QPA_EGLFS_PHYSICAL_HEIGHT para proporcionar los valores que faltan. Esto ya no es adecuado cuando hay varias pantallas. En su lugar, utilice las propiedades physicalWidth y physicalHeight de la lista outputs para especificar los tamaños en milímetros.
Nota: Se desaconseja utilizar diferentes tamaños físicos y, por lo tanto, diferentes DPI lógicos, ya que puede dar lugar a problemas inesperados debido a que algunos componentes de la pila de gráficos no conocen la existencia de múltiples pantallas y se basan únicamente en los valores de la primera pantalla.
Salidas activas e instancias QScreen
Cada salida activa del array outputs corresponde a una instancia de QScreen reportada desde QGuiApplication::screens(). Por defecto, la pantalla principal que reporta QGuiApplication::primaryScreen() es la pantalla que se registra primero. Si no está utilizando virtualIndex, esto significa que la decisión se basa en el orden del conector DRM. Para anular esto, establezca la propiedad primary a true en la entrada deseada de la lista outputs.
Por ejemplo, para asegurarse de que la pantalla correspondiente a la salida VGA es la principal aunque el sistema informe primero de la HDMI, haga lo siguiente:
{
"device": "/dev/dri/card0",
"outputs": [
{ "name": "HDMI1" },
{ "name": "VGA1", "mode": "1280x720", "primary": true },
{ "name": "LVDS1", "mode": "off" }
]
}Para solucionar problemas, puede ser útil habilitar los registros de depuración del backend KMS/DRM. Para ello, active la regla de registro categorizada qt.qpa.eglfs.kms.
Nota: En un entorno integrado, los escritorios virtuales son más limitados en comparación con un sistema de ventanas completo. Las ventanas que se superponen a varias pantallas, las ventanas que no son de pantalla completa y las ventanas que se mueven entre pantallas deben evitarse y es posible que no funcionen como se espera.
Un caso de uso común
El caso de uso más común y mejor soportado para una configuración multipantalla es abrir un QQuickWindow o QQuickView dedicado para cada pantalla. Con el bucle de renderizado por defecto threaded del scenegraph Qt Quick, cada una de estas ventanas obtendrá su propio hilo de renderizado dedicado. Esto es bueno porque los hilos pueden ser acelerados independientemente basados en vsync, y no interferirán unos con otros. Con el bucle basic, esto puede ser problemático, causando que las animaciones se degraden.
Por ejemplo, descubrir todas las pantallas conectadas y crear un QQuickView para cada una de ellas puede hacerse así:
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
QVector<QQuickView *> views;
for (QScreen *screen : app.screens()) {
QQuickView *view = new QQuickView;
view->setScreen(screen);
view->setResizeMode(QQuickView::SizeRootObjectToView);
view->setSource(QUrl("qrc:/main.qml"));
QObject::connect(view->engine(), &QQmlEngine::quit, qGuiApp, &QCoreApplication::quit);
views.append(view);
view->showFullScreen();
}
int result = app.exec();
qDeleteAll(views);
return result;
}Funciones avanzadas de eglfs_kms
Clonación (mirroring)
La clonación de pantallas (mirroring) está soportada. Esto se activa a través de la propiedad clones:
{
"device": "/dev/dri/card0",
"outputs": [
{ "name": "HDMI1", "mode": "1920x1080" },
{ "name": "DP1", "mode": "1920x1080", "clones": "HDMI1" }
]
}En este caso, el contenido de la pantalla conectada a través de DisplayPort será el mismo que el de la pantalla HDMI. Esto se consigue escaneando el mismo búfer en ambas.
Sin embargo, esta característica sólo puede funcionar si las resoluciones son las mismas, no hay incompatibilidades en cuanto a los formatos de búfer aceptados, y la aplicación no tiene ninguna salida en la QScreen asociada a un destino clonado. En la práctica, esto último significa que ningún QWindow asociado al QScreen en cuestión - DP1 en el ejemplo - debe realizar nunca una operación QOpenGLContext::swapBuffers(). Depende de la configuración y de la aplicación garantizar esto.
Modo sin cabeza mediante DRM render
Se admite el modo sin cabeza mediante nodos de renderizado DRM. Esto permite realizar computación GPU (OpenGL compute shaders, OpenCL) o renderizado OpenGL fuera de pantalla sin necesidad de privilegios de maestro DRM. En este modo, las aplicaciones pueden funcionar incluso cuando ya hay otro proceso emitiendo a la pantalla.
El simple hecho de cambiar device de /dev/dri/card0 a /dev/dri/renderD128 es inútil por sí solo, ya que hay una serie de operaciones que no se pueden realizar en modo headless. Por lo tanto, esto debe combinarse con una propiedad headless, por ejemplo:
{
"device": "/dev/dri/renderD128",
"headless": "1024x768"
}Hay que tener en cuenta que el tamaño de las ventanas se sigue ajustando al tamaño de la pantalla -ahora virtual-, de ahí la necesidad de especificar un tamaño en la propiedad headless. Tampoco existe el estrangulamiento basado en vsync.
Una vez habilitadas, las aplicaciones tienen dos opciones típicas para realizar el renderizado fuera de pantalla en modo headless:
Utilizar una ventana normal, como una subclase de QOpenGLWindow, con el framebuffer predeterminado de la ventana, es decir, gbm_surface en la práctica:
MyOpenGLWindow w;
w.show(); // will not actually show up on screen
w.grabFramebuffer().save("output.png");O el típico enfoque fuera de pantalla con un FBO adicional:
QOffscreenSurface s;
s.setFormat(ctx.format());
s.create();
ctx.makeCurrent(&s);
QOpenGLFramebufferObject fbo(1024, 768);
fbo.bind();
ctx.functions()->glClearColor(1, 0, 0, 1);
ctx.functions()->glClear(GL_COLOR_BUFFER_BIT);
fbo.toImage().save("output.png");
ctx.doneCurrent();Selección de la API DRM
KMS/DRM puede utilizarse con dos API de DRM diferentes: la heredada y la atómica. La principal ventaja de la API atómica de DRM es que permite varias actualizaciones de planos DRM en el mismo bucle de renderizado, mientras que la API heredada requiere una actualización de plano por vsync.
La API atómica es útil cuando su aplicación necesita mezclar contenido en superposiciones manteniendo todas las actualizaciones dentro del mismo vsync. Aún no todos los dispositivos soportan esta API y podría no estar disponible en algunos dispositivos antiguos. El backend KMS utilizará por defecto la API heredada, pero puedes activar la API atómica DRM con la variable de entorno QT_QPA_EGLFS_KMS_ATOMIC establecida en 1.
También puede ser útil utilizar un framebuffer más pequeño que la resolución de pantalla. Esto es posible con DRM atomic utilizando el parámetro size en el archivo JSON. El siguiente ejemplo utiliza un framebuffer de 1280x720 en un videomode de 3840x2160 :
{
"device": "/dev/dri/card0",
"outputs": [
{ "name": "HDMI1", "mode": "3840x2160", "size": "1280x720", "format": "argb8888" }
]
}EGLFS hot-plug y hot-reload
Si se establece un archivo de configuración KMS a través de QT_QPA_EGLFS_KMS_CONFIG, el archivo al que se hace referencia será vigilado por un QFileSystemWatcher. Cualquier cambio en este archivo provocará que se vuelva a leer y causará las actualizaciones apropiadas, por ejemplo, cambios de diseño, cambios de modo, encendido/apagado de pantallas.
De forma similar, pero estrictamente opcional, se puede configurar QT_QPA_EGLFS_HOTPLUG_ENABLED, que permitirá a QDeviceDiscovery vigilar el dispositivo KMS en busca de cambios, como pantallas conectadas/enchufadas y desconectadas/desenchufadas.
Ambas funciones pueden y suelen utilizarse en paralelo.
Para facilitar adecuadamente los cambios dinámicos que se desencadenan por hot-plug y hot-reload, es importante reaccionar creando y destruyendo ventanas en las respectivas pantallas que aparecen y desaparecen.
Ejemplo de código:
#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>
#include <QScreen>
#include <QQmlContext>
QHash<QString, QQuickView*> screenNameToViewMap;
int main(int argc, char* argv[])
{
QGuiApplication app(argc,argv);
app.setQuitOnLastWindowClosed(false);
auto lRemove = [&](QScreen *screen) {
if (screen->name().compare(QStringLiteral("qt_Headless")) == 0)
return;
if (!screenNameToViewMap.contains(screen->name()))
return;
QQuickView *view = screenNameToViewMap.take(screen->name());
delete view;
};
auto lAdd = [&](QScreen *screen) {
if (screen->handle() == nullptr)
return;
if (screen->name().compare(QStringLiteral("qt_Headless")) == 0)
return;
if (screenNameToViewMap.contains(screen->name()))
return;
QQuickView *view = new QQuickView;
view->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
view->setScreen(screen); // This is not as important as the next line, but good practice
view->setGeometry(screen->geometry()); // This is vital! Otherwise QWindow::show (/QWindowPrivate::create) will change the screen!
view->show();
screenNameToViewMap.insert(screen->name(), view);
};
QObject::connect(&app, &QGuiApplication::screenAdded, &app, lAdd, Qt::QueuedConnection);
QObject::connect(&app, &QGuiApplication::screenRemoved, &app, lRemove);
for (QScreen *screen : app.screens())
lAdd(screen);
return app.exec();
}Tenga en cuenta en el ejemplo anterior la línea relativa a QWindow::setGeometry, ¡esto es crucial para que las ventanas aparezcan en la pantalla a la que están destinadas!
Como las mismas señales existen también para QML, el código equivalente puede escribirse también en QML.
Especialmente para QT_QPA_EGLFS_HOTPLUG_ENABLED es vital adoptar su código, ya que algunas pantallas actuarán como si estuvieran desconectadas cuando se apaguen. Sin esta característica habilitada, ni Qt ni el código a nivel de aplicación serán notificados sobre desconexiones. Todos los recursos del backend normalmente permanecerán intactos y por lo tanto la pantalla reaparecerá como si nada hubiera pasado. Con QT_QPA_EGLFS_HOTPLUG_ENABLED, los recursos de Qt en la pantalla (p.e. QScreen, QPlatformScreen, backing-store,..) serán destruidos y serán recreados al conectar (p.e. encendiendo de nuevo la pantalla). Por lo tanto, los recursos de nivel de aplicación (por ejemplo, QWindow) también deben volver a crearse.
La pantalla de respaldo llamada "qt_Headless" está ahí para facilitar cualquier cambio hacia y desde una configuración de pantalla cero. Para ventanas a nivel de aplicación, esta pantalla puede y debe ser ignorada en la mayoría de los casos.
EGLFS con un backend eglfs_kms_egldevice
Este backend, utilizado normalmente en dispositivos Tegra, es similar al backend KMS/DRM mencionado anteriormente, salvo que se basa en las extensiones EGLDevice y EGLStream en lugar de GBM.
Para más detalles técnicos sobre este enfoque, consulte esta presentación.
A partir de Qt 5.7 este backend comparte muchas de sus implementaciones internas con el backend basado en GBM. Esto significa que soporta múltiples pantallas y la configuración avanzada a través de QT_QPA_EGLFS_KMS_CONFIG. Sin embargo, algunas configuraciones, como hwcursor y pbuffers no son aplicables.
Por defecto, el backend elegirá automáticamente la capa EGL correcta para el plano por defecto de cada salida. Cuando sea necesario, esto se puede anular estableciendo la variable de entorno QT_QPA_EGLFS_LAYER_INDEX con el índice de la capa deseada. Este método no admite actualmente múltiples salidas, por lo que su uso debería limitarse a sistemas con una sola pantalla. Para ver qué capas están disponibles y depurar posibles problemas de inicio, active la categoría de registro qt.qpa.eglfs.kms.
En algunos casos, puede ser necesario realizar una configuración del modo de vídeo al iniciar la aplicación, incluso cuando la pantalla informa de que la resolución deseada ya está configurada. Esto normalmente se optimiza, pero si la pantalla permanece apagada, intente establecer la variable de entorno QT_QPA_EGLFS_ALWAYS_SET_MODE a un valor distinto de cero y vuelva a iniciar la aplicación.
Para configurar el comportamiento del objeto EGLStream utilizado por el backend, utilice la variable de entorno QT_QPA_EGLFS_STREAM_FIFO_LENGTH. Esto supone que el sistema de destino admite KHR_stream_fifo. Por defecto, el flujo funciona en modo buzón. Para cambiar al modo FIFO, establezca un valor de 1 o superior. El valor especifica el número máximo de tramas que puede contener el flujo.
En algunos sistemas puede ser necesario apuntar a un plano superpuesto específico a través de un conector predefinido. Forzar simplemente un índice de capa a través de QT_QPA_EGLFS_LAYER_INDEX no realiza la configuración del plano y, por lo tanto, no es adecuado en sí mismo. En su lugar, en tales escenarios especiales, utilice las variables de entorno QT_QPA_EGLFS_KMS_CONNECTOR_INDEX y QT_QPA_EGLFS_KMS_PLANE_INDEX. Cuando éstas se establecen, sólo el conector y el plano especificados estarán en uso, todas las demás salidas serán ignoradas. El backend se encargará de elegir la capa EGL que corresponde al plano deseado, y de configurar el plano.
Entrada táctil en sistemas con múltiples pantallas en KMS/DRM
Las pantallas táctiles requieren consideraciones adicionales en sistemas con múltiples pantallas porque los eventos táctiles tienen que ser enrutados a la pantalla virtual correcta, y esto requiere un mapeo correcto entre las pantallas táctiles y las salidas de pantalla.
La asignación se realiza a través del archivo de configuración JSON especificado en QT_QPA_EGLFS_KMS_CONFIG y descrito en las secciones anteriores. Cuando una propiedad touchDevice está presente en un elemento de la matriz outputs, el valor se trata como un nodo de dispositivo y el dispositivo táctil se asocia con la salida de pantalla en cuestión.
Por ejemplo, suponiendo que nuestra pantalla táctil tiene un nodo de dispositivo de /dev/input/event5 y es una pantalla táctil integrada en el monitor conectado vía HDMI como pantalla secundaria, la siguiente configuración asegura la correcta traducción de eventos táctiles (y de ratón sintetizado):
{
"device": "drm-nvdc",
"outputs": [
{
"name": "HDMI1",
"touchDevice": "/dev/input/event5",
"virtualIndex": 1
},
{
"name": "DP1",
"virtualIndex": 0
}
]
}Nota: En caso de duda, habilite el registro de los subsistemas gráfico y de entrada configurando la variable de entorno QT_LOGGING_RULES=qt.qpa.*=true antes de iniciar la aplicación. Esto ayudará a identificar los nodos de dispositivo de entrada correctos y puede descubrir problemas de configuración de salida que pueden ser difíciles de depurar de otra manera.
Nota: A partir de Qt 5.14, lo anterior sólo es compatible con los backends evdevtouch y libinput. Otras variantes continuarán enrutando eventos a la pantalla primaria. Para forzar el uso de evdevtouch en sistemas donde están disponibles múltiples backends de entrada, establece la variable de entorno QT_QPA_EGLFS_NO_LIBINPUT a 1.
EGLFS con otros backends
Otros backends, que normalmente se basan en apuntar al framebuffer o a una API de composición directamente a través de la implementación EGL del proveedor, normalmente proporcionan soporte limitado o nulo para múltiples pantallas. En las placas basadas en i.MX6 con GPU Vivante, la variable de entorno QT_QPA_EGLFS_FB puede utilizarse para especificar el framebuffer de destino, de forma similar a linuxfb. En la Raspberry Pi, la variable de entorno QT_QPA_EGLFS_DISPMANX_ID se puede utilizar para especificar la pantalla de salida. El valor corresponde a una de las constantes de DISPMANX_ID_, consulte la documentación de Dispmanx. Tenga en cuenta que estos enfoques, a diferencia de KMS / DRM, no suelen permitir la salida a múltiples pantallas de la misma aplicación. Alternativamente, las variables de entorno específicas del controlador o los parámetros del kernel también pueden estar disponibles para controlar el framebuffer utilizado. Consulte la documentación de la placa integrada.
Memoria de vídeo
Los sistemas con una cantidad fija de memoria de vídeo dedicada pueden necesitar un cuidado extra antes de ejecutar aplicaciones Qt basadas en Qt Quick o clases como QOpenGLWidget. La configuración por defecto puede ser insuficiente para este tipo de aplicaciones, especialmente cuando se muestran en una pantalla de alta resolución (por ejemplo, Full HD). En este caso, pueden empezar a fallar de forma inesperada. Se recomienda asegurarse de que hay al menos 128 MB de memoria GPU disponible. Para los sistemas que no tienen una cantidad fija de memoria reservada para la GPU, esto no es un problema.
linuxfb
Utiliza el parámetro fb plugin para especificar el dispositivo framebuffer a utilizar.
Manejadores de señales Unix
Los plugins de plataforma orientados a consola como eglfs y linuxfb instalan por defecto manejadores de señales para capturar interrupciones (SIGINT), suspender y continuar (SIGTSTP, SIGCONT) y terminación (SIGTERM). De esta forma, el teclado, el cursor del terminal y posiblemente otros estados gráficos pueden ser restaurados cuando la aplicación termina o se suspende debido a kill, o Ctrl+C o Ctrl+Z. (aunque terminar o suspender a través del teclado sólo es posible cuando QT_QPA_ENABLE_TERMINAL_KEYBOARD está activado, como se ha descrito anteriormente en la sección Entrada). Sin embargo, en algunos casos capturar SIGINT puede ser indeseable ya que puede entrar en conflicto con la depuración remota, por ejemplo. Por lo tanto, la variable de entorno QT_QPA_NO_SIGNAL_HANDLER se proporciona para optar por no participar en la gestión de todas las señales incorporadas.
Fuentes
Qt normalmente usa fontconfig para proporcionar acceso a las fuentes del sistema. Si fontconfig no está disponible, Qt volverá a usar QBasicFontDatabase. En este caso, las aplicaciones Qt buscarán las fuentes en el directorio lib/fonts de Qt. Qt detectará automáticamente las fuentes pre-renderizadas y las fuentes TrueType. Este directorio puede anularse configurando la variable de entorno QT_QPA_FONTDIR.
Para más información sobre los formatos soportados, ver Qt for Embedded Linux Fonts.
Nota: Qt ya no distribuye fuentes en el directorio lib/fonts. Esto significa que depende de la plataforma (la imagen del sistema) proporcionar las fuentes necesarias.
Plugins de plataforma para sistemas de ventanas en dispositivos Linux embebidos
XCB
Este es el plugin X11 utilizado en plataformas Linux de escritorio normales. En algunos entornos embebidos, que proporcionan X y los archivos de desarrollo necesarios para xcb, este plugin funciona igual que en un PC de escritorio normal.
Nota: En algunos dispositivos no hay soporte EGL y OpenGL disponible bajo X porque la implementación EGL no es compatible con Xlib. En este caso el plugin XCB está construido sin soporte EGL, lo que significa que Qt Quick 2 u otras aplicaciones basadas en OpenGL no funcionan con este plugin de plataforma. Sin embargo, puede utilizarse para ejecutar aplicaciones renderizadas por software (basadas en QWidget, por ejemplo).
Como regla general, el uso de XCB en dispositivos embebidos no es aconsejable. Plugins como eglfs pueden proporcionar un mejor rendimiento y aceleración por hardware.
Wayland
Wayland es un sistema de ventanas ligero; o más exactamente, es un protocolo para que los clientes se comuniquen con un servidor de visualización.
Qt Wayland proporciona un plugin para la plataforma wayland que permite a las aplicaciones Qt conectarse a un compositor Wayland.
Para más detalles, véase Wayland y Qt.
Pautas para mejorar el rendimiento
Utilice el renderizado por hardware siempre que sea posible
Cuando el rendimiento sea crítico para su aplicación, evite el uso de módulos Qt que dependan del renderizado por software. Prefiera, siempre que sea posible, módulos que dependan de la renderización por hardware.
Siga las mejores prácticas para Qt Quick
Siga las prácticas recomendadas para QML y Qt Quick, especialmente en lo que respecta a la inclusión de la API CMake de QML, de modo que qmllint, el compilador de scripts de QML (qmlsc) y el compilador de tipos de QML (qmltc) estén disponibles. Además, es preferible escribir QML declarativo y minimizar Javascript. Para obtener más información sobre cómo el uso excesivo de JavaScript puede afectar al rendimiento, consulte Consideraciones y sugerencias sobre el rendimiento de QML.
Utilice imágenes/texturas y efectos de sombreado en lugar del tipo Canvas QML
Para dibujar elementos de interfaz de usuario personalizados, utilice imágenes/texturas y efectos de sombreado. No utilice el tipo QML Canvas. Los shaders requieren aceleración por hardware (GPU).
Utilice Qt Quick en lugar de Qt Widgets
Con Qt Quick, es posible utilizar backends acelerados por hardware o renderizados por software. Para Uls complejas, no se recomienda utilizar Qt Widgets en objetivos embebidos, ya que siempre utilizará un backend de software.
En este caso hay ventajas y desventajas:
- Utilizar el motor QML y Qt Quick conlleva una sobrecarga inicial.
- Si tu interfaz de usuario es muy simple y se repinta raramente, puede funcionar más rápido si se implementa utilizando Widgets en lugar de QML.
- Si tu IU se beneficia de animaciones, smooth scrolling,y scaling, rendering effects, o 3D necesitas disponer de aceleración GPU, y por tanto Qt Quick.
Elige una resolución adecuada al tamaño de tu IU
Hay que tener cuidado con las resoluciones más altas. Las resoluciones de 720p y superiores pueden reducir el rendimiento.
Utiliza un tipo de ventana QML como elemento raíz de tu aplicación.
Utilice un Window como elemento raíz de su aplicación con el fondo de la aplicación color.
El razonamiento para esto es que un componente Window tiene una propiedad de color que tiene el efecto de ser un buffer claro. Renderizar el fondo usando un Rectangle a pantalla completa como raíz de la aplicación Item causaría una llamada de dibujo adicional. Para algunos backends RHI esto puede ser lo mismo, pero hay una diferencia entre una llamada a glClear y dibujar un quad. En la mayoría de los casos, una sola imagen opaca podría no tener un gran impacto en el rendimiento, pero si tuvieras que utilizar un valor alfa en el color de ese elemento, podrías ver un impacto significativo en el rendimiento.
Temas relacionados
© 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.