Compartilhar via


DXGI (Infraestrutura Gráfica do DirectX): Práticas recomendadas

A DXGI (DirectX Graphic Infrastructure) da Microsoft é um novo subsistema que foi introduzido com o Windows Vista que encapsula algumas das tarefas de baixo nível que são necessárias pelo Direct3D 10, 10.1, 11 e 11.1. Da perspectiva de um programador do Direct3D 9, o DXGI abrange a maior parte do código para enumeração, criação de cadeia de troca e apresentação que anteriormente foi empacotada nas APIs do Direct3D 9. Ao portar um aplicativo para DXGI e Direct3D 10.x e Direct3D 11.x, você precisa levar em conta algumas considerações para garantir que o processo seja executado sem problemas.

Este artigo discute os principais problemas de portabilidade.

problemas de Full-Screen

Na portabilidade do Direct3D 9 para o DXGI e para o Direct3D 10.x ou Direct3D 11.x, problemas associados à mudança do modo de janela para o modo de tela inteira geralmente podem causar dores de cabeça para os desenvolvedores. Os problemas main surgem porque os aplicativos Direct3D 9, ao contrário dos aplicativos DXGI, exigem uma abordagem mais prática para acompanhar estilos de janela e estados de janela. Quando o código de alteração de modo é portado para ser executado no DXGI, ele geralmente causa um comportamento inesperado.

Muitas vezes, os aplicativos Direct3D 9 manipulavam a transição para o modo de tela inteira definindo a resolução do buffer frontal, forçando o dispositivo para o modo exclusivo de tela inteira e, em seguida, definindo as resoluções de buffer de fundo para corresponder. Um caminho separado foi usado para alterações no tamanho da janela porque elas tinham que ser gerenciadas do processo de janela sempre que o aplicativo recebia uma mensagem WM_SIZE.

O DXGI tenta simplificar essa abordagem combinando os dois casos. Por exemplo, quando a borda da janela é arrastada no modo de janela, o aplicativo recebe uma mensagem WM_SIZE. O DXGI intercepta essa mensagem e redimensiona automaticamente o buffer frontal. Tudo o que o aplicativo precisa fazer é chamar IDXGISwapChain::ResizeBuffers para redimensionar o buffer de fundo para o tamanho que foi passado como parâmetros no WM_SIZE. Da mesma forma, quando o aplicativo precisa alternar entre o modo de tela inteira e janela, o aplicativo pode simplesmente chamar IDXGISwapChain::SetFullscreenState. O DXGI redimensiona o buffer frontal para corresponder ao modo de tela inteira recém-selecionado e envia uma mensagem de WM_SIZE para o aplicativo. O aplicativo novamente chama ResizeBuffers, assim como faria se a borda da janela fosse arrastada.

A metodologia da explicação anterior segue um caminho muito específico. O DXGI define a resolução de tela inteira para a resolução da área de trabalho por padrão. Muitos aplicativos, no entanto, alternam para uma resolução de tela inteira preferencial. Nesse caso, o DXGI fornece IDXGISwapChain::ResizeTarget. Isso deve ser chamado antes de chamar SetFullscreenState. Embora esses métodos possam ser chamados na ordem oposta (SetFullscreenState primeiro, seguido por ResizeTarget), isso faz com que uma mensagem de WM_SIZE extra seja enviada ao aplicativo. (Fazer isso também pode causar cintilação, pois o DXGI pode ser forçado a executar duas alterações de modo.) Depois de chamar SetFullscreenState, é aconselhável chamar ResizeTarget novamente com o membro RefreshRate de DXGI_MODE_DESC zerado. Isso equivale a uma instrução sem operação no DXGI, mas pode evitar problemas com a taxa de atualização, que são discutidas em seguida.

Quando estiver no modo de tela inteira, o DWM (Gerenciador de Janelas da Área de Trabalho) será desabilitado. O DXGI pode executar uma inversão para apresentar o conteúdo do buffer traseiro em vez de fazer um blit, o que ele faria no modo de janela. No entanto, esse ganho de desempenho poderá ser desfeito se determinados requisitos não forem atendidos. Para garantir que o DXGI faça uma inversão em vez de um blit, o buffer frontal e o buffer traseiro devem ser dimensionados de forma idêntica. Se o aplicativo tratar corretamente suas mensagens de WM_SIZE, isso não deverá ser um problema. Além disso, os formatos devem ser idênticos.

O problema para a maioria dos aplicativos é a taxa de atualização. A taxa de atualização especificada na chamada para ResizeTarget deve ser uma taxa de atualização enumerada pelo objeto IDXGIOutput que a cadeia de troca está usando. O DXGI poderá calcular automaticamente esse valor se o aplicativo zerar o membro RefreshRate de DXGI_MODE_DESC que é passado para ResizeTarget. É importante não assumir que certas taxas de atualização sempre terão suporte e simplesmente codificar um valor. Muitas vezes, os desenvolvedores escolhem 60 Hz como a taxa de atualização, sem saber que a taxa de atualização enumerada do monitor é de aproximadamente 60.000/1.001 Hz do monitor. Se a taxa de atualização não corresponder à taxa de atualização esperada de 60, o DXGI será forçado a executar um blit no modo de tela inteira em vez de uma inversão.

O último problema que os desenvolvedores geralmente enfrentam é como alterar as resoluções de tela inteira enquanto permanecem no modo de tela inteira. Às vezes, chamar ResizeTarget e SetFullscreenState é bem-sucedida, mas a resolução de tela inteira continua sendo a resolução da área de trabalho. Além disso, os desenvolvedores podem criar uma cadeia de troca de tela inteira e fornecer uma resolução específica, apenas para descobrir que o DXGI usa como padrão a resolução da área de trabalho, independentemente dos números passados. A menos que seja instruído de outra forma, o DXGI usa como padrão a resolução da área de trabalho para cadeias de troca de tela inteira. Ao criar uma cadeia de troca de tela inteira, o membro Flags da estrutura DXGI_SWAP_CHAIN_DESC deve ser definido como DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH para substituir o comportamento padrão do DXGI. Esse sinalizador também pode ser passado para ResizeTarget para habilitar ou desabilitar essa funcionalidade dinamicamente.

Vários monitores

Ao usar o DXGI com vários monitores, há duas regras a seguir.

A primeira regra se aplica à criação de duas ou mais cadeias de troca de tela inteira em vários monitores. Ao criar essas cadeias de troca, é melhor criar todas as cadeias de troca como janelas e defini-las como tela inteira. Se as cadeias de troca forem criadas no modo de tela inteira, a criação de uma segunda cadeia de troca fará com que uma alteração de modo seja enviada para a primeira cadeia de troca, o que pode causar o encerramento do modo de tela inteira.

A segunda regra se aplica a saídas. Fique atento às saídas usadas ao criar cadeias de troca. Com o DXGI, o objeto IDXGIOutput controla quais monitoram a cadeia de troca usa ao se tornar tela inteira. Ao contrário do DXGI, o Direct3D 9 não tinha nenhum conceito de saídas.

Estilos de janela e DXGI

Os aplicativos Direct3D 9 tinham muito trabalho a fazer ao alternar entre modos de tela inteira e janelas. Grande parte desse trabalho envolveu a alteração de estilos de janela para adicionar e remover bordas, adicionar barras de rolagem e assim por diante. Quando os aplicativos são portados para DXGI e Direct3D 10.x ou Direct3D 11.x, esse código geralmente é deixado no lugar. Dependendo das alterações que estão sendo feitas, alternar entre modos pode causar um comportamento inesperado. Por exemplo, ao alternar para o modo de janela, o aplicativo pode não ter mais um quadro de janela ou borda de janela, apesar de ter um código que define especificamente esses estilos. Isso ocorre porque o DXGI agora lida com grande parte desse estilo mudando por conta própria. A configuração manual de estilos de janela pode interferir no DXGI e isso pode causar um comportamento inesperado.

O comportamento recomendado é fazer o mínimo de trabalho possível e permitir que o DXGI lide com a maior parte da interação com as janelas. No entanto, se o aplicativo precisar lidar com seu próprio comportamento de janela, IDXGIFactory::MakeWindowAssociation poderá ser usado para instruir o DXGI a desabilitar parte de seu tratamento automático de janela.

Multithreading e DXGI

Deve-se ter um cuidado especial ao usar a DXGI em um aplicativo multithread para garantir que não ocorram deadlocks. Devido à interação próxima do DXGI com janelas, ele ocasionalmente envia mensagens de janela para a janela do aplicativo associada. O DXGI precisa que as alterações de janela ocorram antes de continuar, portanto, ele usará SendMessage, que é uma chamada síncrona. O aplicativo deve processar a mensagem da janela antes que SendMessage retorne.

Em um aplicativo em que as chamadas DXGI e a bomba de mensagem estão no mesmo thread (ou em um aplicativo de thread único), pouco precisa ser feito. Quando a chamada DXGI está no mesmo thread que a bomba de mensagem, SendMessage chama WindowProc da janela. Isso ignora a bomba de mensagem e permite que a execução continue após a chamada para SendMessage. Lembre-se de que chamadas IDXGISwapChain , como IDXGISwapChain::P resent, também são consideradas chamadas DXGI; O DXGI pode adiar o trabalho de ResizeBuffers ou ResizeTarget até que Present seja chamado.

Se a chamada DXGI e a bomba de mensagem estiverem em threads diferentes, deve-se tomar cuidado para evitar deadlocks. Quando a bomba de mensagem e SendMessage estão em threads diferentes, SendMessage adiciona uma mensagem à fila de mensagens da janela e aguarda a janela processar essa mensagem. Se o procedimento da janela estiver ocupado ou não for chamado pela bomba de mensagem, a mensagem poderá nunca ser processada e o DXGI aguardará indefinidamente.

Por exemplo, se um aplicativo que tem sua bomba de mensagem em um thread e sua renderização em outro, talvez ele queira alterar os modos. O thread da bomba de mensagem informa ao thread de renderização para alterar os modos e aguarda até que a alteração do modo seja concluída. No entanto, o thread de renderização chama funções DXGI, que, por sua vez, chamam SendMessage, que bloqueia até que a bomba de mensagem processe a mensagem. Um deadlock ocorre porque os dois threads agora estão bloqueados e estão aguardando um pelo outro. Para evitar isso, nunca bloqueie a bomba de mensagem. Se um bloco for inevitável, toda a interação DXGI deverá ocorrer no mesmo thread que a bomba de mensagem.

Gama e DXGI

Embora o gama possa ser melhor tratado no Direct3D 10.x ou direct3D 11.x usando texturas SRGB, a rampa gama ainda pode ser útil para desenvolvedores que desejam um valor gama diferente de 2.2 ou que estão usando um formato de destino de renderização que não dá suporte a SRGB. Lembre-se de dois problemas ao definir a rampa gama por meio do DXGI. O primeiro problema é que os valores de rampa passados para IDXGIOutput::SetGammaControl são valores float, não valores word . Além disso, verifique se o código portado do Direct3D 9 não tenta converter em valores word antes de passá-los para SetGammaControl.

O segundo problema é que, depois de alterar para o modo de tela inteira, SetGammaControl pode não parecer funcionar, dependendo do objeto IDXGIOutput que está sendo usado. Ao alterar para o modo de tela inteira, o DXGI cria um novo objeto de saída e usa o objeto para todas as operações subsequentes na saída. Se chamar SetGammaControl em uma saída enumerada antes de uma opção de modo de tela inteira, a chamada não será direcionada para a saída que o DXGI está usando no momento. Para evitar isso, chame IDXGISwapChain::GetContainingOutput para obter a saída atual e, em seguida, chame SetGammaControl dessa saída para obter o comportamento correto.

Para obter informações sobre como usar a correção gama, consulte Usando a correção gama.

DXGI 1.1

O runtime do Direct3D 11 incluído no Windows 7 e instalado no Windows Vista inclui a versão 1.1 do DXGI. Essa atualização adiciona definições para vários novos formatos (particularmente BGRA, viés X2 de 10 bits e compactação de textura BC6H e BC7 do Direct3D 11), bem como uma nova versão das interfaces de fábrica e adaptador DXGI (CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1) para enumerar conexões de área de trabalho remota.

Quando você usa o Direct3D 11, o runtime usará o DXGI 1.1 por padrão ao chamar D3D11CreateDevice ou D3D11CreateDeviceAndSwapChain com um ponteiro NULL IDXGIAdapter . Não há suporte para a combinação do uso de DXGI 1.0 e DXGI 1.1 no mesmo processo. Também não há suporte para a combinação de instâncias de objeto DXGI de fábricas diferentes no mesmo processo. Portanto, quando você usa o DirectX 11, qualquer uso explícito das interfaces DXGI usa um IDXGIFactory1 criado pelo ponto de entrada CreateDXGIFactory1 em "DXGI.DLL" para garantir que o aplicativo esteja sempre usando o DXGI 1.1.

DXGI 1.2

O runtime do Direct3D 11.1 incluído no Windows 8 também inclui a versão 1.2 do DXGI.

O DXGI 1.2 habilita estes recursos:

  • renderização estéreo

  • Formatos de 16 bits por pixel

    • DXGI_FORMAT_B5G6R5_UNORM e DXGI_FORMAT_B5G5R5A1_UNORM agora têm suporte total
    • um novo formato de DXGI_FORMAT_B5G5R5A1_UNORM foi adicionado
  • formatos de vídeo

  • novas interfaces DXGI

Para obter mais informações sobre os recursos do DXGI 1.2, consulte Aprimoramentos do DXGI 1.2.