Introducción a DXGI

Microsoft DirectX Graphics Infrastructure (DXGI) reconoce que algunas partes de gráficos evolucionan más lentamente que otras. El objetivo principal de DXGI es administrar tareas de bajo nivel que pueden ser independientes del entorno de ejecución de gráficos de DirectX. DXGI proporciona un marco común para futuros componentes gráficos; el primer componente que aprovecha DXGI es Microsoft Direct3D 10.

En versiones anteriores de Direct3D, tareas de bajo nivel, como la enumeración de dispositivos de hardware, la presentación de fotogramas representados en una salida, el control gamma y la administración de una transición de pantalla completa se incluían en el tiempo de ejecución de Direct3D. Estas tareas ahora se implementan en DXGI.

El propósito de DXGI es comunicarse con el controlador de modo kernel y el hardware del sistema, como se muestra en el diagrama siguiente.

diagrama de la comunicación entre aplicaciones, dxgi y controladores y hardware

Una aplicación puede acceder directamente a DXGI o llamar a las API de Direct3D en D3D11_1.h, D3D11.h, D3D10_1.h o D3D10.h, que controla las comunicaciones con DXGI para usted. Es posible que quiera trabajar con DXGI directamente si la aplicación necesita enumerar dispositivos o controlar cómo se presentan los datos a una salida.

En este tema se incluyen las siguientes secciones.

Para ver qué formatos son compatibles con el hardware de Direct3D 11:

Enumeración de adaptadores

Un adaptador es una abstracción del hardware y la funcionalidad de software del equipo. Por lo general, hay muchos adaptadores en la máquina. Algunos dispositivos se implementan en hardware (como la tarjeta de vídeo) y algunos se implementan en software (como el rasterizador de referencia de Direct3D). Los adaptadores implementan la funcionalidad usada por una aplicación gráfica. En el diagrama siguiente se muestra un sistema con un solo equipo, dos adaptadores (tarjetas de vídeo) y tres monitores de salida.

diagrama de un equipo con dos tarjetas de vídeo y tres monitores

Al enumerar estas piezas de hardware, DXGI crea una interfaz IDXGIOutput1 para cada salida (o monitor) y una interfaz IDXGIAdapter2 para cada tarjeta de vídeo (incluso si es una tarjeta de vídeo integrada en una placa base). La enumeración se realiza mediante una llamada de interfaz IDXGIFactory , IDXGIFactory::EnumAdapters, para devolver un conjunto de interfaces IDXGIAdapter que representan el hardware de vídeo.

DXGI 1.1 agregó la interfaz IDXGIFactory1 . IDXGIFactory1::EnumAdapters1 devuelve un conjunto de interfaces IDXGIAdapter1 que representa el hardware de vídeo.

Si quieres seleccionar funcionalidades específicas de hardware de vídeo al usar las API de Direct3D, te recomendamos que llames iterativamente a la función D3D11CreateDevice o D3D11CreateDeviceAndSwapChain con cada controlador de adaptador y posible nivel de característica de hardware. Esta función se realiza correctamente si el adaptador especificado admite el nivel de característica.

Nueva información sobre la enumeración de adaptadores para Windows 8

A partir de Windows 8, siempre está presente un adaptador denominado "Microsoft Basic Render Driver". Este adaptador tiene un VendorId de 0x1414 y un DeviceID de 0x8c. Este adaptador también tiene el valor DXGI_ADAPTER_FLAG_SOFTWARE establecido en el miembro Flags de su estructura DXGI_ADAPTER_DESC2 . Este adaptador es un dispositivo de solo representación que no tiene salidas de presentación. DXGI nunca devuelve DXGI_ERROR_DEVICE_REMOVED para este adaptador.

Si el controlador de pantalla de un equipo no funciona o está deshabilitado, es posible que el adaptador principal (NULL) del equipo también se llame "Microsoft Basic Render Driver". Pero este adaptador tiene salidas y no tiene establecido el valor DXGI_ADAPTER_FLAG_SOFTWARE . El sistema operativo y las aplicaciones usan este adaptador de forma predeterminada. Si un controlador de pantalla está instalado o habilitado, las aplicaciones pueden recibir DXGI_ERROR_DEVICE_REMOVED para este adaptador y, a continuación, volver a enumerar los adaptadores.

Cuando el adaptador de pantalla principal de un equipo es el "Adaptador de pantalla básico de Microsoft" (adaptador WARP ), ese equipo también tiene un segundo adaptador. Este segundo adaptador es el dispositivo de solo representación que no tiene salidas de presentación y para los que DXGI nunca devuelve DXGI_ERROR_DEVICE_REMOVED.

Si desea usar WARP para representar, calcular u otras tareas de larga duración, se recomienda usar el dispositivo de solo representación. Puede obtener un puntero al dispositivo de solo representación llamando al método IDXGIFactory1::EnumAdapters1 . También se crea el dispositivo de solo representación al especificar D3D_DRIVER_TYPE_WARP en el parámetro DriverType de D3D11CreateDevice porque el dispositivo WARP también usa el adaptador WARP de solo representación.

Presentación

El trabajo de la aplicación es representar fotogramas y pedir a DXGI que presente esos fotogramas a la salida. Si la aplicación tiene dos búferes disponibles, puede representar un búfer mientras presenta otro. La aplicación puede requerir más de dos búferes en función del tiempo necesario para representar un fotograma o la velocidad de fotograma deseada para la presentación. El conjunto de búferes creado se denomina cadena de intercambio, como se muestra aquí.

ilustración de una cadena de intercambio

Una cadena de intercambio tiene un búfer frontal y uno o varios búferes de reserva. Cada aplicación crea su propia cadena de intercambio. Para maximizar la velocidad de la presentación de los datos en una salida, casi siempre se crea una cadena de intercambio en la memoria de un subsistema de visualización, que se muestra en la siguiente ilustración.

Ilustración de un subsistema de visualización

El subsistema de pantalla (que suele ser una tarjeta de vídeo, pero que se puede implementar en una placa base) contiene una GPU, un convertidor digital a analógico (DAC) y memoria. La cadena de intercambio se asigna dentro de esta memoria para hacer que la presentación sea muy rápida. El subsistema de visualización presenta los datos en el búfer frontal a la salida.

Se configura una cadena de intercambio para dibujar en modo de pantalla completa o en ventana, lo que elimina la necesidad de saber si una salida está en ventana o en pantalla completa. Una cadena de intercambio en modo de pantalla completa puede optimizar el rendimiento cambiando la resolución de pantalla.

Crear una cadena de intercambio

Diferencias entre Direct3D 9 y Direct3D 10: Direct3D 10 es el primer componente gráfico para usar DXGI. DXGI tiene algunos comportamientos de cadena de intercambio diferentes.
  • En DXGI, una cadena de intercambio está vinculada a una ventana cuando se crea la cadena de intercambio. Este cambio mejora el rendimiento y ahorra memoria. Las versiones anteriores de Direct3D permitían que la cadena de intercambio cambiara la ventana a la que está vinculada la cadena de intercambio.
  • En DXGI, una cadena de intercambio está vinculada a un dispositivo de representación durante la creación. El objeto de dispositivo que devuelven las funciones de dispositivo de creación de Direct3D implementa la interfaz IUnknown . Puede llamar a QueryInterface para consultar la interfaz IDXGIDevice2 correspondiente del dispositivo. Un cambio en el dispositivo de representación requiere que se vuelva a crear la cadena de intercambio.
  • En DXGI, los efectos de intercambio disponibles son DXGI_SWAP_EFFECT_DISCARD y DXGI_SWAP_EFFECT_SEQUENTIAL. A partir de Windows 8 también está disponible el efecto de intercambio DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL. En la tabla siguiente se muestra una asignación de Direct3D 9 al efecto de intercambio DXGI definido.

    Efecto de intercambio D3D9 Efecto de intercambio DXGI
    D3DSWAPEFFECT_DISCARD DXGI_SWAP_EFFECT_DISCARD
    D3DSWAPEFFECT_COPY DXGI_SWAP_EFFECT_SEQUENTIAL con 1 búfer
    D3DSWAPEFFECT_FLIP DXGI_SWAP_EFFECT_SEQUENTIAL con 2 o más búferes
    D3DSWAPEFFECT_FLIPEX DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL con 2 o más búferes

Los búferes de una cadena de intercambio se crean en un tamaño determinado y en un formato determinado. La aplicación especifica estos valores (o puede heredar el tamaño de la ventana de destino) en el inicio y, a continuación, puede modificarlos opcionalmente a medida que el tamaño de la ventana cambia en respuesta a los eventos de entrada o programa del usuario.

Después de crear la cadena de intercambio, normalmente querrá representar imágenes en ella. Este es un fragmento de código que configura un contexto de Direct3D para representarlo en una cadena de intercambio. Este código extrae un búfer de la cadena de intercambio, crea una vista de destino de representación a partir de ese búfer y, a continuación, la establece en el dispositivo:

ID3D11Resource * pBB;
ThrowFailure( pSwapChain->GetBuffer(0, __uuidof(pBB),    
  reinterpret_cast<void**>(&pBB)), "Couldn't get back buffer");
ID3D11RenderTargetView * pView;
ThrowFailure( pD3D11Device->CreateRenderTargetView(pBB, NULL, &pView), 
  "Couldn't create view" );
pD3D11DeviceContext->OMSetRenderTargets(1, &pView, 0);
        

Después de que la aplicación represente un marco en un búfer de cadena de intercambio, llame a IDXGISwapChain1::P resent1. Después, la aplicación puede ir a representar la siguiente imagen.

Cuidado y alimentación de la cadena de intercambio

Después de representar la imagen, llame a IDXGISwapChain1::P resent1 y vaya a representar la siguiente imagen. Esa es la extensión de su responsabilidad.

Si anteriormente llamó a IDXGIFactory::MakeWindowAssociation, el usuario puede presionar la combinación de teclas de Alt-Enter y DXGI pasará la aplicación entre el modo de pantalla completa y ventana. Se recomienda IDXGIFactory::MakeWindowAssociation , ya que se desea un mecanismo de control estándar para el usuario.

Aunque no es necesario escribir más código de lo que se ha descrito, algunos pasos sencillos pueden hacer que la aplicación tenga mayor capacidad de respuesta. La consideración más importante es el cambio de tamaño de los búferes de la cadena de intercambio en respuesta al cambio de tamaño de la ventana de salida. Naturalmente, la mejor ruta de la aplicación es responder a WM_SIZE y llamar a IDXGISwapChain::ResizeBuffers, pasando el tamaño contenido en los parámetros del mensaje. Obviamente, este comportamiento hace que la aplicación responda bien al usuario cuando arrastra los bordes de la ventana, pero también es exactamente lo que permite una transición fluida de pantalla completa. La ventana recibirá un mensaje de WM_SIZE cada vez que se produzca dicha transición y llamar a IDXGISwapChain::ResizeBuffers es la posibilidad de volver a asignar el almacenamiento de los búferes para una presentación óptima. Por este motivo, la aplicación es necesaria para liberar las referencias que tiene en los búferes existentes antes de llamar a IDXGISwapChain::ResizeBuffers.

Si no se llama a IDXGISwapChain::ResizeBuffers en respuesta al cambio al modo de pantalla completa (de forma más natural, en respuesta a WM_SIZE), puede impedir la optimización del volteo, donde DXGI puede simplemente intercambiar qué búfer se muestra, en lugar de copiar los datos de una pantalla completa alrededor.

IDXGISwapChain1::P resent1 le informará si la ventana de salida está completamente ocluida a través de DXGI_STATUS_OCCLUDED. Cuando esto ocurre, se recomienda que la aplicación entre en modo de espera (llamando a IDXGISwapChain1::P resent1 con DXGI_PRESENT_TEST) ya que se desperdician los recursos usados para representar el fotograma. El uso de DXGI_PRESENT_TEST impedirá que se presenten datos mientras se sigue realizando la comprobación de oclusión. Una vez que IDXGISwapChain1::P resent1 devuelve S_OK, debe salir del modo de espera; no use el código de retorno para cambiar al modo en espera, ya que puede dejar que la cadena de intercambio no pueda renunciar al modo de pantalla completa.

El entorno de ejecución de Direct3D 11.1, que está disponible a partir de Windows 8, proporciona una cadena de intercambio de modelo invertido (es decir, una cadena de intercambio que tiene el valor DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL establecido en el miembro SwapEffect de DXGI_SWAP_CHAIN_DESC o DXGI_SWAP_CHAIN_DESC1). Cuando se presentan fotogramas a una salida con una cadena de intercambio de modelo invertido, DXGI desenlace el búfer de reserva de todas las ubicaciones de estado de la canalización, como un destino de representación de fusión de salida, que se escribe en el búfer de reserva 0. Por lo tanto, se recomienda llamar a ID3D11DeviceContext::OMSetRenderTargets inmediatamente antes de representarlo en el búfer de reserva. Por ejemplo, no llame a OMSetRenderTargets y, a continuación, realice el trabajo del sombreador de proceso que no termine de representarse en el recurso. Para obtener más información sobre las cadenas de intercambio de modelo invertida y sus ventajas, consulta DXGI Flip Model.

Nota

En Direct3D 10 y Direct3D 11, no tienes que llamar a IDXGISwapChain::GetBuffer para recuperar el búfer 0 después de llamar a IDXGISwapChain1::P resent1 porque para mayor comodidad cambian las identidades de los búferes de reserva. Esto no sucede en Direct3D 12 y la aplicación debe realizar un seguimiento manual de los índices de búfer de reserva.

Control del cambio de tamaño de la ventana

Puede usar el método IDXGISwapChain::ResizeBuffers para controlar el cambio de tamaño de la ventana. Antes de llamar a ResizeBuffers, debe liberar todas las referencias pendientes a los búferes de la cadena de intercambio. El objeto que normalmente contiene una referencia al búfer de una cadena de intercambio es una vista de destino de representación.

En el código de ejemplo siguiente se muestra cómo llamar a ResizeBuffers desde el controlador WindowProc para WM_SIZE mensajes:

    case WM_SIZE:
        if (g_pSwapChain)
        {
            g_pd3dDeviceContext->OMSetRenderTargets(0, 0, 0);

            // Release all outstanding references to the swap chain's buffers.
            g_pRenderTargetView->Release();

            HRESULT hr;
            // Preserve the existing buffer count and format.
            // Automatically choose the width and height to match the client rect for HWNDs.
            hr = g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
                                            
            // Perform error handling here!

            // Get buffer and create a render-target-view.
            ID3D11Texture2D* pBuffer;
            hr = g_pSwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D),
                                         (void**) &pBuffer );
            // Perform error handling here!

            hr = g_pd3dDevice->CreateRenderTargetView(pBuffer, NULL,
                                                     &g_pRenderTargetView);
            // Perform error handling here!
            pBuffer->Release();

            g_pd3dDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL );

            // Set up the viewport.
            D3D11_VIEWPORT vp;
            vp.Width = width;
            vp.Height = height;
            vp.MinDepth = 0.0f;
            vp.MaxDepth = 1.0f;
            vp.TopLeftX = 0;
            vp.TopLeftY = 0;
            g_pd3dDeviceContext->RSSetViewports( 1, &vp );
        }
        return 1;

Elección de la salida y el tamaño de DXGI

De forma predeterminada, DXGI elige la salida que contiene la mayoría del área de cliente de la ventana. Esta es la única opción disponible para DXGI cuando se dirige a pantalla completa en respuesta a alt-entrar. Si la aplicación elige ir al modo de pantalla completa por sí misma, puede llamar a IDXGISwapChain::SetFullscreenState y pasar un IDXGIOutput1 explícito (o NULL, si la aplicación está feliz de permitir que DXGI decida).

Para cambiar el tamaño de la salida mientras se muestra la pantalla completa o la ventana, se recomienda llamar a IDXGISwapChain::ResizeTarget, ya que este método también cambia el tamaño de la ventana de destino. Dado que se cambia el tamaño de la ventana de destino, el sistema operativo envía WM_SIZE y el código llamará naturalmente a IDXGISwapChain::ResizeBuffers en respuesta. Por lo tanto, es un desperdicio de esfuerzo para cambiar el tamaño de los búferes y, a continuación, cambiar el tamaño del destino.

Depuración en modo de pantalla completa

Una cadena de intercambio DXGI renuncia al modo de pantalla completa solo cuando es absolutamente necesario. Esto significa que puede depurar una aplicación de pantalla completa mediante varios monitores, siempre y cuando la ventana de depuración no se superponga a la ventana de destino de la cadena de intercambio. Como alternativa, puedes evitar el cambio de modo por completo si no estableces la marca DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH .

Si se permite el cambio de modo, una cadena de intercambio renunciará al modo de pantalla completa cada vez que otra ventana de salida se ocluye. La comprobación de oclusión se realiza durante IDXGISwapChain1::P resent1, o mediante un subproceso independiente cuyo propósito es watch para ver si la aplicación deja de responder (y ya no llama a IDXGISwapChain1::P resent1). Para deshabilitar la capacidad del subproceso independiente para provocar un modificador, establezca la siguiente clave del Registro en cualquier valor distinto de cero.

HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog

Destrucción de una cadena de intercambio

Es posible que no libere una cadena de intercambio en modo de pantalla completa porque, al hacerlo, puede crear contención de subprocesos (lo que hará que DXGI genere una excepción no continuable). Antes de liberar una cadena de intercambio, cambie primero al modo con ventana (mediante IDXGISwapChain::SetFullscreenState( FALSE, NULL )) y, a continuación, llame a IUnknown::Release.

Uso de un monitor girado

Una aplicación no necesita preocuparse por la orientación del monitor, DXGI rotará un búfer de cadena de intercambio durante la presentación, si es necesario. Por supuesto, esta rotación adicional puede afectar al rendimiento. Para obtener el mejor rendimiento, cuide la rotación en la aplicación mediante lo siguiente:

  • Use el DXGI_SWAP_CHAIN_FLAG_NONPREROTATED. Esto notifica a DXGI que la aplicación generará una imagen rotada (por ejemplo, modificando su matriz de proyección). Una cosa que hay que tener en cuenta, esta marca solo es válida mientras está en modo de pantalla completa.
  • Asigne cada búfer de cadena de intercambio en su tamaño girado. Use IDXGIOutput::GetDesc para obtener estos valores, si es necesario.

Al realizar la rotación en la aplicación, DXGI simplemente realizará una copia en lugar de una copia y un giro.

El entorno de ejecución de Direct3D 11.1, que está disponible a partir de Windows 8, proporciona una cadena de intercambio de modelo invertido (es decir, una cadena de intercambio que tiene el valor DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL establecido en el miembro SwapEffect de DXGI_SWAP_CHAIN_DESC1). Para maximizar las optimizaciones de presentación disponibles con una cadena de intercambio de modelo invertido, se recomienda que las aplicaciones orienten el contenido para que coincida con la salida concreta en la que reside el contenido cuando ese contenido ocupa completamente la salida. Para obtener más información sobre las cadenas de intercambio de modelo invertida y sus ventajas, consulta DXGI Flip Model.

Cambio de modos

La cadena de intercambio DXGI podría cambiar el modo de presentación de una salida al realizar una transición de pantalla completa. Para habilitar el cambio automático del modo de presentación, debe especificar DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH en la descripción de la cadena de intercambio. Si el modo de presentación cambia automáticamente, DXGI elegirá el modo más modesto (el tamaño y la resolución no cambiarán, pero la profundidad del color puede). Cambiar el tamaño de los búferes de cadena de intercambio no provocará un cambio de modo. La cadena de intercambio promete implícitamente que si elige un búfer de reserva que coincida exactamente con un modo de presentación admitido por la salida de destino, cambiará a ese modo de presentación al entrar en modo de pantalla completa en esa salida. Por lo tanto, elige un modo de presentación eligiendo el tamaño y el formato del búfer de reserva.

Sugerencia de rendimiento de pantalla completa

Cuando se llama a IDXGISwapChain1::P resent1 en una aplicación de pantalla completa, la cadena de intercambio voltea (en contraposición a las ranuras) el contenido del búfer de reserva en el búfer frontal. Esto requiere que la cadena de intercambio se haya creado mediante un modo de presentación enumerado (especificado en DXGI_SWAP_CHAIN_DESC1). Si no enumera los modos de visualización o especifica incorrectamente el modo de presentación en la descripción, la cadena de intercambio puede realizar una transferencia de bloques de bits (bitblt) en su lugar. El bitblt provoca una copia de extensión adicional, así como un mayor uso de memoria de vídeo, y es difícil de detectar. Para evitar este problema, enumere los modos de visualización e inicialice correctamente la descripción de la cadena de intercambio antes de crear la cadena de intercambio. Esto garantizará el máximo rendimiento al voltear en modo de pantalla completa y evitará la sobrecarga de memoria adicional.

Consideraciones de varios subprocesos

Al usar DXGI en una aplicación con varios subprocesos, debe tener cuidado de evitar la creación de un interbloqueo, donde dos subprocesos diferentes están esperando entre sí para completarse. Hay dos situaciones en las que esto puede ocurrir.

  • El subproceso de representación no es el subproceso de bomba de mensajes.
  • El subproceso que ejecuta una API DXGI no es el mismo subproceso que creó la ventana.

Tenga cuidado de que nunca tenga que esperar el subproceso de bomba de mensajes en el subproceso de representación cuando use cadenas de intercambio de pantalla completa. Por ejemplo, llamar a IDXGISwapChain1::P resent1 (desde el subproceso de representación) puede hacer que el subproceso de representación espere en el subproceso de bomba de mensajes. Cuando se produce un cambio de modo, este escenario es posible si Present1 llama a ::SetWindowPos() o ::SetWindowStyle() y cualquiera de estos métodos llama a ::SendMessage(). En este escenario, si el subproceso de bomba de mensajes tiene una sección crítica que lo protege o si el subproceso de representación está bloqueado, los dos subprocesos se interbloquearán.

Para obtener más información sobre el uso de DXGI con varios subprocesos, consulta Multithreading y DXGI.

Respuestas DXGI de DLLMain

Dado que una función DllMain no puede garantizar el orden en el que carga y descarga archivos DLL, se recomienda que la función DllMain de la aplicación no llame a funciones o métodos Direct3D o DXGI, incluidas funciones o métodos que crean o liberan objetos. Si la función DllMain de la aplicación llama a un componente determinado, ese componente podría llamar a otro archivo DLL que no está presente en el sistema operativo, lo que hace que el sistema operativo se bloquee. Direct3D y DXGI pueden cargar un conjunto de archivos DLL, normalmente un conjunto de controladores, que difiere del equipo al equipo. Por lo tanto, incluso si la aplicación no se bloquea en los equipos de desarrollo y prueba cuando su función DllMain llama a las funciones o métodos de Direct3D o DXGI, podría bloquearse cuando se ejecuta en otro equipo.

Para evitar que cree una aplicación que pueda provocar que el sistema operativo se bloquee, DXGI proporciona las siguientes respuestas en las situaciones especificadas:

  • Si la función DllMain de la aplicación libera su última referencia a un generador de DXGI, DXGI genera una excepción.
  • Si la función DllMain de la aplicación crea una factoría DXGI, DXGI devuelve un código de error.

Cambios de DXGI 1.1

Hemos agregado la siguiente funcionalidad en DXGI 1.1.

Cambios de DXGI 1.2

Hemos agregado la siguiente funcionalidad en DXGI 1.2.

  • Cadena de intercambio estéreo
  • Cadena de intercambio de modelo invertida
  • Presentación optimizada (desplazamiento, rectángulos sucios y rotación)
  • Se han mejorado los recursos compartidos y la sincronización.
  • Duplicación de escritorio
  • Uso optimizado de la memoria de vídeo
  • Compatibilidad con formatos de 16 bits por píxel (bpp) (DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B4G4R4A4_UNORM)
  • API de depuración

Para obtener más información sobre DXGI 1.2, consulta DXGI 1.2 Improvements.

Guía de programación para DXGI