Infraestructura de gráficos de DirectX (DXGI): Procedimientos recomendados
Microsoft DirectX Graphics Infrastructure (DXGI) es un nuevo subsistema que se introdujo con Windows Vista que encapsula algunas de las tareas de bajo nivel que necesita Direct3D 10, 10.1, 11 y 11.1. Desde la perspectiva de un programador de Direct3D 9, DXGI abarca la mayoría del código para la enumeración, la creación de cadenas de intercambio y la presentación que anteriormente se empaquetaba en las API de Direct3D 9. Al migrar una aplicación a DXGI y Direct3D 10.x y Direct3D 11.x, debes tener en cuenta algunas consideraciones para asegurarte de que el proceso se ejecuta sin problemas.
En este artículo se describen los problemas de portabilidad clave.
- Problemas de pantalla completa
- Varios monitores
- Estilos de ventana y DXGI
- Multithreading y DXGI
- Gamma y DXGI
- DXGI 1.1
- DXGI 1.2
problemas de Full-Screen
Al migrar de Direct3D 9 a DXGI y a Direct3D 10.x o Direct3D 11.x, los problemas asociados con el traslado de ventanas al modo de pantalla completa a menudo pueden causar dolores de cabeza para los desarrolladores. Los principales problemas surgen porque las aplicaciones de Direct3D 9, a diferencia de las aplicaciones DXGI, requieren un enfoque más práctico para realizar el seguimiento de los estilos de ventana y los estados de ventana. Cuando el código de cambio de modo se traslada para ejecutarse en DXGI, a menudo provoca un comportamiento inesperado.
A menudo, las aplicaciones de Direct3D 9 controlaron la transición al modo de pantalla completa estableciendo la resolución del búfer frontal, forzando el dispositivo al modo exclusivo de pantalla completa y estableciendo las resoluciones del búfer de reserva para que coincidan. Se usó una ruta de acceso independiente para los cambios en el tamaño de la ventana porque tenían que administrarse desde el proceso de ventana cada vez que la aplicación recibió un mensaje de WM_SIZE.
DXGI intenta simplificar este enfoque combinando los dos casos. Por ejemplo, cuando el borde de la ventana se arrastra en modo de ventana, la aplicación recibe un mensaje de WM_SIZE. DXGI intercepta este mensaje y cambia automáticamente el tamaño del búfer frontal. Todo lo que la aplicación necesita hacer es llamar a IDXGISwapChain::ResizeBuffers para cambiar el tamaño del búfer de reserva al tamaño que se pasó como parámetros en WM_SIZE. De forma similar, cuando la aplicación necesita cambiar entre el modo de pantalla completa y de ventana, la aplicación simplemente puede llamar a IDXGISwapChain::SetFullscreenState. DXGI cambia el tamaño del búfer frontal para que coincida con el modo de pantalla completa recién seleccionado y envía un mensaje WM_SIZE a la aplicación. La aplicación llama de nuevo a ResizeBuffers, como lo haría si se arrastrara el borde de la ventana.
La metodología de la explicación anterior sigue una ruta muy concreta. DXGI establece la resolución de pantalla completa en la resolución de escritorio de forma predeterminada. Sin embargo, muchas aplicaciones cambian a una resolución de pantalla completa preferida. En tal caso, DXGI proporciona IDXGISwapChain::ResizeTarget. Se debe llamar a esto antes de llamar a SetFullscreenState. Aunque se puede llamar a estos métodos en el orden opuesto (SetFullscreenState primero, seguido de ResizeTarget), esto hace que se envíe un mensaje de WM_SIZE adicional a la aplicación. (Si lo hace, también puede provocar parpadeo, ya que DXGI podría verse obligado a realizar dos cambios en el modo). Después de llamar a SetFullscreenState, es aconsejable llamar a ResizeTarget de nuevo con el miembro RefreshRate de DXGI_MODE_DESC a cero. Esto equivale a una instrucción sin operación en DXGI, pero puede evitar problemas con la frecuencia de actualización, que se tratan a continuación.
Cuando se encuentra en modo de pantalla completa, el Administrador de ventanas de escritorio (DWM) está deshabilitado. DXGI puede realizar un volteo para presentar el contenido del búfer de reserva en lugar de hacer unalit, lo que haría en modo de ventana. Sin embargo, esta ganancia de rendimiento se puede deshacer si no se cumplen ciertos requisitos. Para asegurarse de que DXGI realiza un volteo en lugar de una ranura, el búfer frontal y el búfer trasero deben tener un tamaño idéntico. Si la aplicación controla correctamente sus mensajes de WM_SIZE, no debería ser un problema. Además, los formatos deben ser idénticos.
El problema de la mayoría de las aplicaciones es la frecuencia de actualización. La frecuencia de actualización especificada en la llamada a ResizeTarget debe ser una frecuencia de actualización enumerada por el objeto IDXGIOutput que está usando la cadena de intercambio. DXGI puede calcular automáticamente este valor si la aplicación cero el miembro RefreshRate de DXGI_MODE_DESC que se pasa a ResizeTarget. Es importante no suponer que ciertas tasas de actualización siempre se admitirán y simplemente codificar de forma rígida un valor. A menudo, los desarrolladores eligen 60 Hz como velocidad de actualización, sin saber que la frecuencia de actualización enumerada del monitor es de aproximadamente 60 000 / 1001 Hz desde el monitor. Si la frecuencia de actualización no coincide con la frecuencia de actualización esperada de 60, DXGI se ve obligado a realizar una blit en modo de pantalla completa en lugar de un volteo.
El último problema al que a menudo se enfrentan los desarrolladores es cómo cambiar las resoluciones de pantalla completa mientras permanecen en modo de pantalla completa. Llamar a ResizeTarget y SetFullscreenState a veces se realiza correctamente, pero la resolución de pantalla completa sigue siendo la resolución de escritorio. Además, los desarrolladores pueden crear una cadena de intercambio de pantalla completa y proporcionar una resolución específica, solo para encontrar que DXGI tiene como valor predeterminado la resolución de escritorio independientemente de los números pasados. A menos que se indique lo contrario, DXGI tiene como valor predeterminado la resolución de escritorio para cadenas de intercambio de pantalla completa. Al crear una cadena de intercambio de pantalla completa, el miembro Flags de la estructura DXGI_SWAP_CHAIN_DESC debe establecerse en DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH para invalidar el comportamiento predeterminado de DXGI. Esta marca también se puede pasar a ResizeTarget para habilitar o deshabilitar esta funcionalidad dinámicamente.
Varios monitores
Al usar DXGI con varios monitores, hay dos reglas que se deben seguir.
La primera regla se aplica a la creación de dos o más cadenas de intercambio de pantalla completa en varios monitores. Al crear estas cadenas de intercambio, es mejor crear todas las cadenas de intercambio como ventanas y, a continuación, establecerlas en pantalla completa. Si las cadenas de intercambio se crean en modo de pantalla completa, la creación de una segunda cadena de intercambio hace que se envíe un cambio de modo a la primera cadena de intercambio, lo que podría provocar la finalización del modo de pantalla completa.
La segunda regla se aplica a las salidas. Tenga cuidado con las salidas que se usan al crear cadenas de intercambio. Con DXGI, el objeto IDXGIOutput controla los controles que supervisan la cadena de intercambio cuando se convierten en pantalla completa. A diferencia de DXGI, Direct3D 9 no tenía ningún concepto de salidas.
Estilos de ventana y DXGI
Las aplicaciones de Direct3D 9 tenían mucho trabajo que hacer al cambiar entre los modos de pantalla completa y ventana. Gran parte de este trabajo implica cambiar los estilos de ventana para agregar y quitar bordes, para agregar barras de desplazamiento, etc. Cuando las aplicaciones se trasladan a DXGI y Direct3D 10.x o Direct3D 11.x, este código a menudo se deja en su lugar. En función de los cambios que se realicen, el cambio entre modos puede provocar un comportamiento inesperado. Por ejemplo, al cambiar al modo de ventana, es posible que la aplicación ya no tenga un borde de ventana o marco de ventana a pesar de tener código que establezca específicamente estos estilos. Esto ocurre porque DXGI ahora controla gran parte de este estilo cambiando por sí mismo. La configuración manual de los estilos de ventana puede interferir con DXGI y esto puede provocar un comportamiento inesperado.
El comportamiento recomendado es hacer el menor trabajo posible y permitir que DXGI controle la mayor parte de la interacción con las ventanas. Sin embargo, si la aplicación necesita controlar su propio comportamiento de ventanas, IDXGIFactory::MakeWindowAssociation se puede usar para indicar a DXGI que deshabilite parte de su control automático de ventanas.
Multithreading y DXGI
Se debe tener especial cuidado al usar DXGI en una aplicación multiproceso para asegurarse de que no se produzcan interbloqueos. Debido a la estrecha interacción de DXGI con la ventana, en ocasiones envía mensajes de ventana a la ventana de aplicación asociada. DXGI necesita que se produzcan cambios en las ventanas para poder continuar, por lo que usará SendMessage, que es una llamada sincrónica. La aplicación debe procesar el mensaje de ventana antes de que SendMessage devuelva.
En una aplicación donde las llamadas DXGI y la bomba de mensajes se encuentran en el mismo subproceso (o en una aplicación de un solo subproceso), es necesario realizar poco. Cuando la llamada DXGI está en el mismo subproceso que la bomba de mensajes, SendMessage llama al WindowProc de la ventana. Esto omite la bomba de mensajes y permite que la ejecución continúe después de la llamada a SendMessage. Recuerde que las llamadas IDXGISwapChain , como IDXGISwapChain::P resent, también se consideran llamadas DXGI; DXGI puede aplazar el trabajo de ResizeBuffers o ResizeTarget hasta que se llame a Present .
Si la llamada DXGI y la bomba de mensajes están en subprocesos diferentes, se debe tener cuidado para evitar interbloqueos. Cuando la bomba de mensajes y SendMessage están en subprocesos diferentes, SendMessage agrega un mensaje a la cola de mensajes de la ventana y espera a que la ventana procese ese mensaje. Si el procedimiento de ventana está ocupado o no lo llama la bomba de mensajes, es posible que el mensaje nunca se procese y DXGI esperará indefinidamente.
Por ejemplo, si una aplicación que tiene su bomba de mensajes en un subproceso y su representación en otro, puede que desee cambiar los modos. El subproceso de bomba de mensajes indica al subproceso de representación que cambie los modos y espera hasta que se complete el cambio de modo. Sin embargo, el subproceso de representación llama a funciones DXGI, que a su vez llaman a SendMessage, que se bloquea hasta que la bomba de mensajes procesa el mensaje. Se produce un interbloqueo porque ambos subprocesos están bloqueados y están esperando entre sí. Para evitar esto, nunca bloquee la bomba de mensajes. Si un bloque es inevitable, toda la interacción DXGI debe producirse en el mismo subproceso que la bomba de mensajes.
Gamma y DXGI
Aunque gamma se puede controlar mejor en Direct3D 10.x o Direct3D 11.x mediante texturas SRGB, la rampa gamma todavía puede ser útil para los desarrolladores que desean un valor gamma diferente a 2.2 o que usan un formato de destino de representación que no admite SRGB. Tenga en cuenta dos problemas al establecer la rampa gamma a través de DXGI. El primer problema es que los valores de rampa pasados a IDXGIOutput::SetGammaControl son valores flotantes, no valores de WORD . Además, asegúrese de que el código portado desde Direct3D 9 no intenta convertir en valores de WORD antes de pasarlos a SetGammaControl.
El segundo problema es que, después de cambiar al modo de pantalla completa, es posible que SetGammaControl no funcione, dependiendo del objeto IDXGIOutput que se esté usando. Al cambiar al modo de pantalla completa, DXGI crea un nuevo objeto de salida y usa el objeto para todas las operaciones posteriores en la salida. Si llama a SetGammaControl en una salida que se enumera antes de un modificador de modo de pantalla completa, la llamada no se dirige hacia la salida que DXGI usa actualmente. Para evitar esto, llame a IDXGISwapChain::GetContainingOutput para obtener la salida actual y, a continuación, llame a SetGammaControl de esta salida para obtener el comportamiento correcto.
Para obtener información sobre el uso de la corrección gamma, consulta Uso de la corrección gamma.
DXGI 1.1
El entorno de ejecución de Direct3D 11 incluido en Windows 7 e instalado en Windows Vista incluye la versión 1.1 de DXGI. Esta actualización agrega definiciones para varios formatos nuevos (especialmente BGRA, sesgo X2 de 10 bits y compresión de textura BC6H y BC7 de Direct3D 11), así como una nueva versión de las interfaces de fábrica y adaptador de DXGI (CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1) para enumerar las conexiones de escritorio remoto.
Cuando se usa Direct3D 11, el runtime usará DXGI 1.1 de forma predeterminada al llamar a D3D11CreateDevice o D3D11CreateDeviceAndSwapChain con un puntero IDXGIAdapter NULL. No se admite el uso de mezcla de DXGI 1.0 y DXGI 1.1 en el mismo proceso. Tampoco se admite la combinación de instancias de objetos DXGI de fábricas diferentes en el mismo proceso. Por lo tanto, cuando se usa DirectX 11, cualquier uso explícito de las interfaces DXGI usa un IDXGIFactory1 creado por el punto de entrada CreateDXGIFactory1 en "DXGI.DLL" para asegurarse de que la aplicación siempre usa DXGI 1.1.
DXGI 1.2
El entorno de ejecución de Direct3D 11.1 que se incluye en Windows 8 también incluye la versión 1.2 de DXGI.
DXGI 1.2 permite estas características:
representación estéreo
Formatos de 16 bits por píxel
- DXGI_FORMAT_B5G6R5_UNORM y DXGI_FORMAT_B5G5R5A1_UNORM ahora son totalmente compatibles
- Se agregó un nuevo formato de DXGI_FORMAT_B5G5R5A1_UNORM
formatos de vídeo
nuevas interfaces DXGI
Para obtener más información sobre las características de DXGI 1.2, consulta DXGI 1.2 Improvements.