Programação DirectX

O Windows 8.1 introduz o DirectX 11.2, que traz uma série de novos recursos para melhorar o desempenho de jogos e aplicativos gráficos.

Novidades e atualizações do Windows 8.1

  • Vinculação do sombreador HLSL
  • Compilador HLSL da caixa de entrada
  • Suporte à sobreposição de GPU
  • Recursos DirectX em bloco
  • API de apresentação de baixa latência do Direct3D
  • API de corte DXGI e buffer padrão de mapeamento
  • Colocação em escala de buffer de quadro
  • Multithreading com SurfaceImageSource
  • Composição interativa do Microsoft DirectX de elementos visuais XAML
  • Envio em lote Direct2D com SurfaceImageSource

Vinculação do sombreador HLSL

[Obtenha o exemplo de compilador sombreador HLSL agora.]

O Windows 8.1 adiciona a compilação e vinculação separadas de sombreadores HLSL. Com isso, os programadores gráficos podem criar funções HLSL pré-compiladas, empacotá-las em bibliotecas e vinculá-las a sombreadores completos em tempo de execução. Basicamente, isso equivale à compilação, às bibliotecas e à vinculação separada do C/C++ e permite que os programadores escrevam código HLSL pré-compilado, quando há mais informações disponíveis para finalizar a computação.

Os programadores executam várias etapas para criar um sombreador final usando a ligação dinâmica em tempo de execução:

  • Criar um objeto vinculador ID3D11Linker, que representa um contexto de vinculação. Não é possível usar um único contexto para produzir vários sombreadores. Um contexto de vinculação é usado para produzir um único sombreador e depois o contexto de vinculação é descartado.

  • Use D3DLoadModule para carregar e definir bibliotecas a partir de seus blobs de bibliotecas.

  • Use D3DLoadModule para carregar e definir um blob de sombreador de entrada ou criar um sombreador FLG (veja a próxima seção).

  • Use ID3D11Module::CreateInstance para criar objetos ID3D11ModuleInstance, depois chame funções nesses objetos para reassociar recursos a seus slots finais.

  • Adicione as bibliotecas ao vinculador e chame ID3D11Linker::Link para produzir o código de bytes do sombreador final que pode então ser carregado e usado nos tempos de execução D3D atuais da mesma forma que um sombreador totalmente pré-compilado e vinculado.

FLG (Function Linking Graph)

O Windows 8.1 também adiciona o FLG (Function Linking Graph). O FLG permite que os programadores construam sombreadores, que são uma sequência de invocações de funções pré-compiladas que passam valores umas para as outras. Ao usar o FLG, não é preciso escrever o HLSL, nem invocar o compilador HLSL. Em vez disso, a estrutura do sombreador é especificada de forma programática usando chamadas à API C++. Os nós do FLG representam assinaturas de entrada e saídas e invocações de funções de bibliotecas pré-compiladas. A ordem de registro dos nós função-chamada define a sequência das invocações. Primeiro é preciso especificar o nó da assinatura de entrada e depois o nó da assinatura de saída. As bordas do FLG definem como os valores são passados de um nó para outro. Os tipos de dados de valores passados devem ser iguais; não existe conversão implícita de tipos. As regras de forma e conversão seguem o comportamento HLSL e os valores só podem ser passados nessa sequência. Para saber sobre a API FLG, veja ID3D11FunctionLinkingGraph.

Compilador HLSL da caixa de entrada

[Obtenha o exemplo de compilador sombreador HLSL agora.]

O compilador HLSL agora é a caixa de entrada no Windows 8.1 e posteriores. Agora, a maioria das APIs de programação de sombreadores pode ser usada em aplicativos da Windows Store criados para o Windows 8.1 e versões posteriores. Muitas APIs de programação de sombreadores não podiam ser usadas em aplicativos da Windows Store criados para o Windows 8; as páginas de referência dessas APIs foram marcadas com uma observação. Entretanto, algumas APIs de sombreador (por exemplo, D3DCompileFromFile) ainda podem ser usadas só para desenvolver aplicativos da Windows Store, e não em aplicativos que você envia para a Windows Store; as páginas de referência dessas APIs ainda estão marcadas com uma observação.

Suporte à sobreposição de GPU

[Obtenha as Cadeias de Permuta de Primeiro Plano DirectX agora.]

O suporte à sobreposição de GPU em vários planos confere a melhor aparência de suas maravilhosas artes e interfaces 2D em resolução nativa, enquanto você desenha suas cenas 3D em um buffer de quadros dimensionado em tamanho menor. Use o novo método IDXGIOutput2::SupportsOverlays para ver se a plataforma é compatível com vários planos de desenho. Crie uma nova cadeia de permuta de sobreposição especificando o sinalizador DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER (consulte DXGI_SWAP_CHAIN_FLAG).

Este recurso permite o dimensionamento e a composição de duas cadeias de permuta que ocorrem automaticamente no hardware de sobreposições de função fixa, sem usar nenhum recurso de GPU. Assim, sobram mais recursos de GPU para o código de renderização do aplicativo. Os aplicativos podem usar código para a renderização em várias resoluções, mas isso envolve cópias extra e passos de composição que não são os ideais em plataformas de baixo consumo de energia.

Você pode usar a API IDXGISwapChain2::SetSourceSize para redimensionar a cadeia de permuta '3D' inferior para uma resolução inferior à nativa, enquanto cria uma nova cadeia de permuta de 'primeiro plano' em resolução nativa com o conteúdo '2D'. Assim, você controla o dimensionamento das duas cadeias de permuta.

Recursos DirectX em bloco

[Obtenha o exemplo de Recursos Lado a Lado Direct3D agora.]

O Windows 8.1 apresenta um novo recurso Direct3D, chamado de recursos em bloco, que expõe um modelo de memória gráfica virtual limitada para os aplicativos, permitindo assim o mapeamento flexível entre dados de recursos lógicos e a memória física. Assim, é possível criar grandes recursos lógicos que usam pequenas quantidades de memória física. Isso é útil (por exemplo) na topografia de jogos e na interface do usuário de aplicativos.

Os recursos em bloco são criados especificando o sinalizador D3D11_RESOURCE_MISC_TILED. Veja as seguintes funções de API para trabalhar com recursos em bloco: UpdateTiles, UpdateTileMappings e GetResourceTiling.

Suporte a recursos em bloco do DirectX

Para usar os recursos em bloco, você precisa descobrir se o dispositivo é compatível com recursos em bloco. Confira o suporte a recursos em bloco da seguinte maneira:

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 apresentação de baixa latência do Direct3D

[Obtenha o exemplo de DirectXLatency agora.]

O Windows 8.1 inclui um novo conjunto de APIs para aplicativos DirectX, para que apresentem quadros com uma latência menor, permitindo uma resposta mais rápida da interface do usuário. Usando as novas APIs, os aplicativos podem aguardar a apresentação da cadeia de permuta DXGI, em vez da cadeia de permuta aguardar no aplicativo. Assim como no comportamento existente 'bloquear na apresentação', a latência de quadros desejada pode ser ajustada usando chamadas à API.

Usando a API de apresentação de baixa latência

Os programadores podem usar a API de apresentação de baixa latência da seguinte maneira.

  • Crie a cadeia de permuta com o sinalizador 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().
    
  • As cadeias de permuta criadas com o sinalizador DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT usam sua própria configuração de latência por cadeia de permuta e não aquela associada com o dispositivo DXGI. A latência padrão por cadeia de permuta é 1, o que garante que a DXGI não coloque na fila mais de um quadro por vez. Isso reduz a latência e também garante que o aplicativo execute a renderização somente depois de cada VSync, minimizando o consumo de energia.

    Existe uma troca entre a configuração de latência e o trabalho de CPU/GPU que pode ser executado em paralelo. Se o trabalho em série da CPU e da GPU excede 17 milissegundos (ms), ele deve mudar a latência de quadro para 2, o que dá tempo suficiente para que o trabalho da CPU/GPU seja executado em paralelo. Se o seu aplicativo realizará mais de 17 ms de trabalho por quadro, defina a latência como 2 chamando SetMaximumFrameLatency.

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

    Depois, chame GetFrameLatencyWaitableObject para obter o objeto de espera da latência de quadro da cadeia de permuta.

    m_frameLatencyWaitableObject = m_swapChain->GetFrameLatencyWaitableObject();
    

    No loop de renderização, não esqueça de chamar WaitForSingleObjectEx no início de cada loop. Assim, o aplicativo espera a cadeia de permuta estar disponível para o novo trabalho de renderização antes do aplicativo começar a renderizar cada novo quadro.

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

Colocação em escala de buffer de quadro

A nova colocação em escala de GPU permite redimensionar dinamicamente o buffer de quadro para manter seus gráficos 3D suaves. A nova interface IDXGISwapChain2 define um conjunto de novos métodos para obter e definir o tamanho da cadeia de permuta de origem e definir a matriz de colocação em escala para seu buffer de quadro.

API de corte DXGI e buffer padrão de mapeamento

[Obtenha o exemplo de rotação de cadeia de troca DXGI agora.]

O Windows 8.1 adiciona o novo método DXGI IDXGIDevice3::Trim, que permite que os aplicativos DirectX liberem memória de dispositivo alocada pelo driver de gráficos, reduzindo assim o perfil de memória do aplicativo enquanto ele está suspenso.

O Windows 8.1 também inclui uma nova operação de buffer padrão de mapeamento para aplicativos Direct3D. Ela permite que o seu aplicativo acesse diretamente os buffers padrão da GPU (se o dispositivo for compatível), sem precisar de uma operação de cópia intermediária mais cara para um buffer temporário.

Multithreading com SurfaceImageSource

[Obtenha o exemplo de interoperabilidade de DirectX SurfaceImageSource XAML agora.]

No Windows 8.1, os aplicativos também podem acessar e atualizar elementos XAML SurfaceImageSource (SIS) de um thread em segundo plano.

Agora os aplicativos podem chamar ISurfaceImageSourceNativeWithD2D::BeginDraw de qualquer thread. Os novos métodos ISurfaceImageSourceNativeWithD2D:SuspendDraw e ISurfaceImageSourceNativeWithD2D:ResumeDraw também permitem que os aplicativos suspendam o desenho em um thread e retomem em outro.

Para aproveitar o multithreading, seu aplicativo deve atender a estas condições:

  • Estes objetos (se usados) devem ser multithreaded:

  • O aplicativo chama BeginDraw, SuspendDraw e ResumeDraw em qualquer thread, mas só chama EndDraw no thread da interface do usuário XAML. Veja que os aplicativos não precisam chamar ResumeDraw antes de chamar EndDraw, e que todas as atualizações de EndDraw enviadas no mesmo thread da interface do usuário XAML são enviados no mesmo quadro.

Veja aqui um exemplo de código que mostra como usar o multithreading com uma 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();

Pontos principais sobre os novos métodos ISurfaceImageSourceNative1::SetDevice1 e ISurfaceImageSourceNative1::BeginDraw1:

  • Diferente de SetDevice(), SetDevice1() aceita como opção um ID2D1Device em vez de um IDXGIDevice. Isso é necessário para dar suporte ao envio em lote Direct2D entre superfícies.

  • A sobrecarga do novo BeginDraw1() retorna um contexto de dispositivo em vez de retornar diretamente uma superfície temporária quando um dispositivo Direct2D foi passado para SetDevice.

Composição interativa do DirectX de elementos visuais XAML

[Obtenha o exemplo de interoperabilidade de DirectX SwapChainPanel XAML agora.]

Você pode usar a classe SwapChainPanel no Windows 8.1 para renderizar o conteúdo gráfico DirectX em um aplicativo que usa XAML, com melhor desempenho e menor latência do que ao usar a classe SurfaceImageSource no Windows 8, e com menos restrições de layout do que ao usar a classe SwapChainBackgroundPanel.

SwapChainPanel é semelhante a SwapChainBackgroundPanel no Windows 8, mas com menos restrições de uso e posicionamento na árvore XAML. (SwapChainBackgroundPanel ficava restrito à exibição em tela inteira e ao posicionamento atrás de todo o conteúdo XAML na árvore de exibição de elementos).

SwapChainPanel dá suporte a as seguintes opções de exibição:

  • Tamanhos, transformações e níveis de opacidade arbitrários

  • Intercalação com outros elementos visuais na árvore XAML

  • Rolagem com o controle ScrollViewer

Seu aplicativo também pode ter várias cadeias de permuta, com cada uma fornecendo a apresentação de um SwapChainPanel individual. Mas saiba que o desempenho pode cair quando muitas cadeias de permuta são atualizadas ao mesmo tempo. Recomendamos que o aplicativo use no máximo quatro cadeias de permuta.

Os objetos SwapChainPanel são declarados como qualquer outro elemento XAML, em qualquer lugar da árvore de elementos e com qualquer tamanho.

Veja como declarar um SwapChainPanel na página XAML.

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

Como com SwapChainBackgroundPanel, os aplicativos criam uma cadeia de permuta de suporte chamando o método IDXGIFactory2::CreateSwapChainForComposition e o associando a uma instância de SwapChainPanel. Essa associação é feita usando um ponteiro inteligente para a interface ISwapChainPanelNative.

Estas condições se aplicam à cadeia de permuta que você cria:

  • A cadeia de permuta pode ter qualquer tamanho considerado válido pela Microsoft DXGI (DirectX Graphics Infrastructure).

  • A cadeia de permuta não é ampliada quando redimensionada pelo usuário. Em vez disso, o comportamento de redimensionamento é parecido com a definição de Stretch=None em um elemento Image.

  • Os aplicativos só podem associar uma cadeia de permuta (usando o método ISwapChainPanelNative::SetSwapChain) no thread da interface do usuário principal (CoreWindow).

  • Uma única cadeia de permuta pode ser associada a vários SwapChainPanels. Os aplicativos podem renderizar e apresentar a cadeia de permuta em qualquer intervalo, do thread da interface do usuário ou de um thread em segundo plano, desde que as condições de multithreading do DirectX sejam atendidas.

Veja aqui um exemplo de código que mostra como associar uma cadeia de permuta a uma instância de SwapChainPanel.

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

Envio em lote Direct2D com SurfaceImageSource

[Obtenha o exemplo de interoperabilidade de DirectX SurfaceImageSource XAML agora.]

Com o Windows 8.1, os aplicativos podem atualizar vários elementos XAML SurfaceImageSource (SIS) e VirtualSurfaceImageSource (VSIS) sem precisar de uma operação em lote Direct2D separada para cada atualização.

Os aplicativos podem aceitar este novo comportamento de envio em lote chamando o método QueryInterface e adquirindo um ponteiro para a nova interface ISurfaceImageSourceNativeWithD2D.

Como com outras APIs de interoperabilidade DirectX-para-XAML, essas são interfaces COM "nativas" e não APIs de Tempo de Execução do Windows, pois interoperam diretamente com as APIs do DirectX. Depois de acessar as novas interfaces, os aplicativos podem chamar o método SetDevice1 em qualquer interface e passar um objeto ID2D1Device ou IDXGIDevice.

Se o aplicativo fornece um dispositivo Direct2D, isso quer dizer que o SIS deve dar suporte ao envio em lote de chamadas Direct2D. Quando BeginDraw é chamado na interface ISurfaceImageSourceNativeWithD2D, o aplicativo recebe um objeto ID2D1DeviceContext em vez de um objeto IDXGISurface. Forneça esse dispositivo para a estrutura XAML, que então cria uma superfície correspondente. Todos os comandos de desenho emitidos para ID2D1DeviceContext são então enviados em lote (quando possível) com comandos de desenho Direct2D para todos os outros elementos SIS que:

Esse novo comportamento é melhor quando um aplicativo atualiza ao mesmo tempo dois elementos SIS com conteúdo Direct2D. O aplicativo usa a nova funcionalidade de envio em lote para compartilhar um contexto e dispositivo Direct2D e assim evitar a liberação de lotes Direct2D entre cada atualização de SIS. A redução do trabalho da CPU melhora o desempenho.

Veja aqui um código que mostra como fazer tudo isso.

// 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());

Observação  Veja a seguir algumas outras dicas: