Programación con DirectX

Windows 8.1 presenta DirectX 11.2, que ofrece un abanico de nuevas características para mejorar el rendimiento de tus aplicaciones de juegos y gráficos.

Novedades o actualizaciones de Windows 8.1

  • Vinculación de sombreador HLSL
  • Compilador HLSL de bandeja de entrada
  • Compatibilidad con superposición de GPU
  • Recursos en mosaico de DirectX
  • API de presentación de latencia baja de Direct3D
  • API de recorte de DXGI y búfer predeterminado de asignación
  • Escalado de búfer de cuadros
  • Multithreading con SurfaceImageSource
  • Composición interactiva de elementos visuales XAML de Microsoft DirectX
  • Procesamiento por lotes de Direct2D con SurfaceImageSource

Vinculación de sombreador HLSL

[Obtén la muestra del compilador de sombreador HLSL ahora.]

Windows 8.1 agrega compilación y vinculación separadas de sombreadores HLSL, lo que permite que los programadores gráficos creen funciones HLSL precompiladas, las empaqueten en bibliotecas y las vinculen con sombreadores completos en tiempo de ejecución. Es esencialmente un equivalente a la vinculación, las bibliotecas y la compilación separada de C/C++, y permite que los programadores compongan código HLSL precompilado cuando haya más información disponible para terminar con el cálculo.

Los programadores realizan varios pasos para crear un sombreador final mediante el uso de vinculación dinámica en tiempo de ejecución:

  • Crea un objeto de vinculador ID3D11Linker, que represente el contexto de vinculación. No puede usarse un único contexto para producir múltiples sombreadores. Se usa un contexto de vinculación para producir un único sombreador y dicho contexto luego se desecha.

  • Usa D3DLoadModule para cargar y configurar bibliotecas desde los blobs de sus bibliotecas.

  • UsaD3DLoadModule para cargar y configurar un blob de sombreador de entrada o crear un sombreador FLG (consulta la sección siguiente).

  • Usa ID3D11Module::CreateInstance para crear objetos ID3D11ModuleInstance y después llamar a las funciones en ellos para reenlazar los recursos en sus ranuras finales.

  • Agrega las bibliotecas al vinculador y después llama a ID3D11Linker::Link para producir el código de byte de sombreador final que después puede cargarse y usarse en los tiempos de ejecución de D3D actuales como si fuera un sombreador vinculado y totalmente precompilado.

Gráfico de vinculación de funciones (FLG)

Windows 8.1 también agrega el gráfico de vinculación de funciones (FLG). El FLG permite a los programadores crear sombreadores que constan de una secuencia de invocaciones de funciones precompiladas que intercambian valores entre ellas. Cuando se usa FLG, no hay necesidad de escribir HLSL ni de invocar el compilador HLSL. Por el contrario, la estructura del sombreador se especifica mediante programación usando llamadas API C++. Los nodos de FLG representan firmas de entrada y salida e invocaciones de funciones de biblioteca precompiladas. El orden del registro de los nodos de llamada de función define la secuencia de las invocaciones. El nodo de firma de entrada debe especificarse primero, mientras que el nodo de firma de salida debe especificarse en último término. Los bordes de FLG definen cómo se pasan los valores de uno nodo a otro. Los tipos de datos de los valores pasados deben ser los mismos. No existe un tipo implícito de conversión. Las reglas de forma y permutación siguen el comportamiento de HLSL y los valores solamente pueden pasarse hacia adelante en esta secuencia. Para obtener información sobre la API de FLG, consulta ID3D11FunctionLinkingGraph.

Compilador HLSL de bandeja de entrada

[Obtén la muestra del compilador de sombreador HLSL ahora.]

El compilador HLSL es ahora la bandeja de entrada en Windows 8.1 y versiones posteriores. La mayoría de las API para la programación de sombreadores puede usarse en aplicaciones de la Tienda Windows compiladas para Windows 8.1 y versiones posteriores. Muchas de las API para la programación de sombreadores no podían usarse en aplicaciones de la Tienda Windows compiladas para Windows 8. Las páginas de referencia para estas API se marcaban con una nota. No obstante, algunas API de sombreador, por ejemplo, D3DCompileFromFile, todavía pueden usarse solamente para desarrollar aplicaciones de la Tienda Windows y no en aplicaciones que envías a dicha tienda. Las páginas de referencia para estas API se siguen marcando con una nota.

Compatibilidad con superposición de GPU

[Obtén las cadenas de intercambio de primer plano de DirectX ahora.]

La compatibilidad con la superposición de múltiples planos de GPU hace que tus preciosas interfaces e imágenes 2D se vean de la mejor manera en una resolución nativa, mientras dibujas tus escenas 3D en un búfer de cuadros en escala, más pequeño. Usa el método IDXGIOutput2::SupportsOverlays nuevo para determinar si la plataforma admite múltiples planos de dibujo. Crea una nueva cadena de intercambio de superposiciones especificando la marca DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER (consulta DXGI_SWAP_CHAIN_FLAG).

Esta característica permite que el escalado y la composición de dos cadenas de intercambio se produzcan automáticamente en el hardware de superposiciones de intercambio de función fija, sin usar ningún recurso de GPU. Así se dejan más recursos de GPU para el código de representación de aplicaciones. Las aplicaciones ya pueden usar código para representarse en múltiples resoluciones, pero esto simplemente implica transferencias de composición y copias extra que no son ideales en plataformas de bajo consumo.

Usa la API IDXGISwapChain2::SetSourceSize para cambiar el tamaño de la cadena de intercambio '3D' inferior a una resolución menor que la nativa, mientras creas una nueva cadena de intercambio de 'primer plano' en la resolución nativa que tenga contenido '2D'. De esta manera, controlas el tamaño de ambas cadenas de intercambio.

Recursos en mosaico de DirectX

[Obtén la muestra de recursos en mosaico de Direct3D ahora.]

Windows 8.1 incluye una nueva característica de Direct3D llamada "recursos en mosaico", que expone un modelo de memoria gráfica virtual limitada a las aplicaciones y de esta manera permite una asignación dinámica entre datos de recursos lógicos y la memoria física. Esto permite la creación de grandes recursos lógicos que usan pequeñas cantidades de memoria física. Esto es útil, por ejemplo, con el terreno de los juegos y la interfaz de usuario de la aplicación.

Los recursos en mosaico se crean especificando la marca D3D11_RESOURCE_MISC_TILED. Consulta las siguientes funciones de API para trabajar con recursos en mosaico: UpdateTiles, UpdateTileMappings y GetResourceTiling.

Compatibilidad con la característica de recursos en mosaico de DirectX

Antes de usar la característica de recursos en mosaico, debes descubrir si el dispositivo los admite. Comprueba la compatibilidad de características para los recursos en mosaico de la siguiente manera:

HRESULT hr = D3D11CreateDevice(
    nullptr,                    // Specify nullptr to use the default adapter.
    D3D_DRIVER_TYPE_HARDWARE,   // Create a device using the hardware graphics driver.
    0,                          // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
    creationFlags,              // Set debug and Direct2D compatibility flags.
    featureLevels,              // List of feature levels this app can support.
    ARRAYSIZE(featureLevels),   // Size of the list above.
    D3D11_SDK_VERSION,          // Always set this to D3D11_SDK_VERSION for Windows Store apps.
    &device,                    // Returns the Direct3D device created.
    &m_d3dFeatureLevel,         // Returns feature level of device created.
    &context                    // Returns the device immediate context.
    );

if (SUCCEEDED(hr))
{
    D3D11_FEATURE_DATA_D3D11_OPTIONS1 featureData;
    DX::ThrowIfFailed(
        device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS1, &featureData, sizeof(featureData))
        );

    m_tiledResourcesTier = featureData.TiledResourcesTier;
}

API de presentación de latencia baja de Direct3D

[Obtén la muestra de DirectXLatency ahora.]

Windows 8.1 incluye un nuevo conjunto de API para que las aplicaciones de DirectX presenten los cuadros con latencia más baja, lo que permite una respuesta más rápida de la interfaz de usuario. Con las nuevas API, las aplicaciones pueden esperar a que la cadena de intercambio de DXGI se presente, en lugar de tener que hacer que la cadena de intercambio espere a la aplicación. Al igual que el comportamiento existente de bloquear al presentar, la latencia de cuadros deseada se ajusta usando llamadas API.

Uso de la API de presentación de latencia baja

Los programadores pueden usar la API de presentación de latencia baja de la siguiente manera.

  • Crea la cadena de intercambio con la marca DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT.

    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; // Enable GetFrameLatencyWaitableObject().
    
  • Las cadenas de intercambio creadas con la marca DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT usan su propia configuración de latencia por cadena de intercambio en lugar de aquella asociada con el dispositivo DXGI. La latencia por cadena de intercambio predeterminada es 1, lo que garantiza que DXGI no ponga en cola más de un cuadro por vez. Esto reduce la latencia y, al mismo tiempo, garantiza que la aplicación solamente se representará después de cada SincV, lo que minimiza el consumo de energía.

    Existe un equilibrio entre la configuración de latencia y la cantidad de trabajo de CPU/GPU que puede realizarse en paralelo. Si el trabajo de CPU y GPU en serie de una aplicación supera los 17 milisegundos (ms), debe cambiarse la latencia de cuadros a 2, lo que proporciona tiempo suficiente para que el trabajo de CPU/GPU se ejecute en paralelo. Si tu aplicación va a realizar más de 17 ms de trabajo por cuadro, establece la latencia en 2 mediante una llamada a SetMaximumFrameLatency.

    DX::ThrowIfFailed(
        m_swapChain->SetMaximumFrameLatency(2)
        );
    

    Luego llama a GetFrameLatencyWaitableObject para obtener el objeto que puede esperar la latencia de cuadros de la cadena de intercambio.

    m_frameLatencyWaitableObject = m_swapChain->GetFrameLatencyWaitableObject();
    

    En el bucle de representación, asegúrate de llamar a WaitForSingleObjectEx al comienzo de cada bucle. Esto garantiza que la aplicación esperará que la cadena de intercambio esté disponible para el nuevo trabajo de representación antes de que la aplicación comience a representar cada cuadro nuevo.

    DWORD result = WaitForSingleObjectEx(
        m_frameLatencyWaitableObject,
        1000, // 1-second timeout (shouldn't ever occur)
        true
        );
    

Escalado de búfer de cuadros

El nuevo escalado de GPU te permite cambiar el tamaño del búfer de cuadros dinámicamente para mantener la suavidad de los gráficos 3D. La nueva interfaz IDXGISwapChain2 define un conjunto de métodos nuevos para obtener y establecer el tamaño de la cadena de intercambio de origen, y definir la matriz de escalado del búfer de cuadros.

API de recorte de DXGI y búfer predeterminado de asignación

[Obtén lamuestra de rotación de cadena de intercambio de DXGI ahora.]

Windows 8.1 agrega el nuevo método IDXGIDevice3::Trim de DXGI, que permite a las aplicaciones DirectX liberar la memoria del dispositivo asignada por el controlador de gráficos, reduciendo de este modo el perfil de memoria de la aplicación mientras está en suspensión.

Además, Windows 8.1 incluye una nueva operación de búfer predeterminado de asignación para aplicaciones de Direct3D que permite que tu aplicación obtenga acceso a los búferes predeterminados de un GPU de forma directa (si el dispositivo lo admite) sin necesidad de una operación de copia intermedia a un búfer temporal más costosa.

Multithreading con SurfaceImageSource

[Obtén la muestra de interoperabilidad de SurfaceImageSource en DirectX y XAML ahora.]

En Windows 8.1, las aplicaciones también pueden obtener acceso y actualizar elementos XAML de SurfaceImageSource (SIS) desde un subproceso en segundo plano.

Las aplicaciones ahora pueden llamar a ISurfaceImageSourceNativeWithD2D::BeginDraw desde cualquier subproceso. Del mismo modo, los nuevos métodos ISurfaceImageSourceNativeWithD2D:SuspendDraw y ISurfaceImageSourceNativeWithD2D:ResumeDraw permiten que las aplicaciones suspendan el dibujo en un subproceso y lo continúen en otro.

Para sacar provecho del multithreading, tu aplicación debe cumplir con estas condiciones:

  • Estos objetos, si se usan, deben ser de multiprocesos:

    • El dispositivo de Microsoft Direct3D (ID3D11Device) y el contexto del dispositivo de Direct3D (ID3D11DeviceContext) (si corresponde).

    • El dispositivo de Direct2D (ID2D1Device).

    • El contexto del dispositivo de Direct2D (ID2D1DeviceContext) (si corresponde o cuando no se procesa por lotes el contenido de Direct2D).

  • La aplicación llama a BeginDraw, SuspendDraw y ResumeDraw en cualquier subproceso, pero llama a EndDraw solamente en el subproceso de la interfaz de usuario de XAML. Ten en cuenta que es necesario que las aplicaciones llamen aResumeDraw antes de llamar a EndDraw y que todas las actualizaciones de EndDraw que se envían dentro de la misma graduación de subproceso de la interfaz de usuario de XAML se enviarán en el mismo cuadro.

He aquí un ejemplo de código que muestra cómo usar multithreading con un SurfaceImageSource.

// Create a SIS element and associate a multithreaded DirectX device
ComPtr<ISurfaceImageSourceNativeWithD2D> m_sisNative;

// ...

m_sisNative->SetDevice1(m_d3dDevice.Get());

POINT offset;
ComPtr<IDXGISurface> surface;

// Begin drawing.
HRESULT beginDrawHR = m_sisNative->BeginDraw1(
updateRect, IID_IDXGISurface, &surface, &offset);

// QI for target texture from DXGI surface.
ComPtr<ID3D11Texture2D> d3DTexture;
surface.As(&d3DTexture);

// Create render target view.
m_d3dDevice->CreateRenderTargetView(d3DTexture.Get(), nullptr,   
  &m_renderTargetView); 

// Render complex content.
// ...

// Suspend drawing before signaling the UI thread.
m_sisNative->SuspendDraw();

// Signal the UI thread that drawing is complete, so EndDraw() can be called.
// ...

// Call EndDraw() on the UI thread to commit changes for the current frame.
m_sisNative->EndDraw();

Puntos clave acerca de los nuevos métodos ISurfaceImageSourceNative1::SetDevice1 y ISurfaceImageSourceNative1::BeginDraw1:

  • A diferencia de SetDevice(), SetDevice1() tendrá la opción de aceptar unID2D1Device en lugar de un IDXGIDevice. Esto es necesario para admitir el procesamiento por lotes de Direct2D a través de distintas superficies.

  • La nueva sobrecarga de BeginDraw1() devuelve un contexto de dispositivo en lugar de devolver directamente una superficie de prueba si se pasó aSetDevice un dispositivo de Direct2D.

Composición interactiva de elementos visuales XAML de DirectX

[Obtén la muestra de interoperabilidad de SwapChainPanel en DirectX y XAML ahora.]

Puedes usar la clase SwapChainPanel en Windows 8.1 para representar gráficos DirectX en una aplicación que use XAML, con un mejor rendimiento y una menor latencia que cuando usabas la clase SurfaceImageSource en Windows 8, y con menos restricciones de diseño que cuando usabas la clase SwapChainBackgroundPanel.

SwapChainPanel es similar a SwapChainBackgroundPanel en Windows 8, pero con menos restricciones en uso y ubicación en el árbol de XAML. (SwapChainBackgroundPanel se restringió a una visualización de pantalla completa y una colocación detrás de todo el contenido XAML en el árbol de visualización de elementos).

SwapChainPanel admite las siguientes opciones de visualización:

  • Niveles de opacidad, transformaciones y tamaños arbitrarios

  • Intercalación con otros elementos visuales en el árbol XAML

  • Desplazamiento con el control ScrollViewer

Tu aplicación también puede tener múltiples cadenas de intercambio, y cada una de ellas puede proporcionar la presentación para un SwapChainPanelindividual. No obstante, ten en cuenta que el rendimiento puede disminuir si se actualizan simultáneamente muchas cadenas de intercambio. Recomendamos que tu aplicación use no más de cuatro cadenas de intercambio.

Los objetos de SwapChainPanel se declaran de la misma manera que cualquier otro elemento XAML, en cualquier parte del árbol de elementos y con cualquier tamaño.

He aquí cómo declaras un SwapChainPanel en tu página XAML.

<Page>
  <Grid>
    <SwapChainPanel x:Name=”mySwapChainPanel” Height=”300” Width=”400” />
    <!-- Additional XAML code ..>
</Grid>
</Page>

Al igual que con SwapChainBackgroundPanel, las aplicaciones crean una cadena de intercambio de respaldo llamando al método IDXGIFactory2::CreateSwapChainForComposition y asociándola con una interfaz de SwapChainPanel. Realizas esta asociación usando un puntero inteligente hacia la interfaz de ISwapChainPanelNative.

Estas condiciones se aplican a la cadena de intercambio que creas:

  • La cadena de intercambio puede tener cualquier tamaño que la infraestructura de gráficos de Microsoft DirectX (DXGI) considere válido.

  • La cadena de intercambio no se estira cuando el usuario cambia su tamaño. En su lugar el comportamiento de cambio de tamaño es similar a la configuración de Stretch=None en un elemento Image.

  • Las aplicaciones puede asociar una cadena de intercambio (a través del método ISwapChainPanelNative::SetSwapChain) solamente en el subproceso de la interfaz de usuario principal (CoreWindow).

  • Una única cadena de intercambio puede asociarse con múltiples SwapChainPanels. Las aplicaciones pueden representarse en la cadena de intercambio y presentarla en cualquier intervalo desde el subproceso de la interfaz de usuario o desde un subproceso en segundo plano, siempre que se cumplan las condiciones de multithreading de DirectX.

He aquí un código de ejemplo que muestra cómo asociar una cadena de intercambio con una instancia de SwapChainPanel.

ComPtr<ISwapChainPanelNative> panelNative;
 
reinterpret_cast<IUnknown*>(mySwapChainPanel)->QueryInterface(IID_PPV_ARGS(&panelNative));
panelNative->SetSwapChain(m_swapChain.Get());

Procesamiento por lotes de Direct2D con SurfaceImageSource

[Obtén la muestra de interoperabilidad de SurfaceImageSource en DirectX y XAML ahora.]

Con Windows 8.1, las aplicaciones pueden actualizar múltiples elementos XAML de SurfaceImageSource (SIS) y VirtualSurfaceImageSource (VSIS) sin necesidad de una operación por lotes de Direct2D separada para cada una de las actualizaciones.

Las aplicaciones pueden optar por este nuevo comportamiento de procesamiento por lotes llamando al método QueryInterface y adquiriendo un puntero a la nueva interfaz de ISurfaceImageSourceNativeWithD2D.

Al igual que con otras API de interoperación de DirectX a XAML, estas son interfaces de COM "nativas" en lugar de API de Windows en tiempo de ejecución porque interoperan con las API de DirectX directamente. Después de obtener acceso a las nuevas interfaces, las aplicaciones pueden llamar al método SetDevice1 en cualquiera de las interfaces y pasar un ID2D1Device o un objeto IDXGIDevice.

Si la aplicación proporciona un dispositivo de Direct2D, esto indica que el SIS debe admitir el procesamiento por lotes de las llamadas de Direct2D. En ese caso, donde se llama a BeginDraw en la interfaz de ISurfaceImageSourceNativeWithD2D, la aplicación recibe un objeto ID2D1DeviceContext en lugar de un objeto IDXGISurface. Proporciona este dispositivo al marco XAML, que luego crea una superficie correspondiente. Después, todos los comandos de dibujo emitidos para el ID2D1DeviceContext se procesan por lotes (cuando es posible) con comandos de dibujo de Direct2D para cualquier otro elemento de SIS que:

Este nuevo comportamiento es mejor para cuando una aplicación actualizará de manera simultánea dos elementos de SIS que tienen contenido de Direct2D. La aplicación usa la nueva funcionalidad de procesamiento por lotes para compartir un dispositivo de Direct2D y su contexto, y así evitar vaciar los lotes de Direct2D entre cada actualización de SIS. Reducir el trabajo de CPU mejora el rendimiento.

He aquí código que muestra cómo hacer todo esto.

// Create SIS elements and associate a multithreaded DirectX device.
ComPtr<ISurfaceImageSourceNativeWithD2D> m_sisNative1;
ComPtr<ISurfaceImageSourceNativeWithD2D> m_sisNative2;

// ...

m_sisNative1->SetDevice1(m_d3dDevice.Get());
m_sisNative2->SetDevice1(m_d3dDevice.Get());

// Set device for both SIS elements.
m_sisNative1->SetDevice(m_d2dDevice.Get());
m_sisNative2->SetDevice(m_d2dDevice.Get());

POINT offset;
ComPtr<ID2D1DeviceContext> d2dContext;

// Begin drawing SIS #1. 
HRESULT beginDrawHR = m_sisNative1->BeginDraw(
updateRect, IID_ID2D1DeviceContext, &d2dContext, &offset);
 
if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || 
    beginDrawHR == DXGI_ERROR_DEVICE_RESET)
{ // Error checking }

// App does not call BeginDraw/EndDraw on Direct2D context;
// this is managed by the framework.

// Issue Direct2D drawing commands for SIS #1.
d2dContext->FillRectangle(D2D1::RectF(0,0,10,10), m_brush1.Get());

// Begin drawing SIS #2. 
beginDrawHR = m_sisNative2->BeginDraw(
updateRect, IID_ID2D1DeviceContext, &d2dContext, &offset);
 
if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || 
    beginDrawHR == DXGI_ERROR_DEVICE_RESET)
{ // Error checking. }

// Issue Direct2D drawing commands for SIS #2.
d2dContext ->FillRectangle(D2D1::RectF(20,20,30,30), m_brush2.Get());

Nota  Estas son algunas sugerencias adicionales: