Visão geral do DXGI

A DXGI (Infraestrutura Gráfica do Microsoft DirectX) reconhece que algumas partes dos gráficos evoluem mais lentamente do que outras. O objetivo principal do DXGI é gerenciar tarefas de baixo nível que podem ser independentes do runtime de elementos gráficos DirectX. O DXGI fornece uma estrutura comum para componentes gráficos futuros; o primeiro componente que aproveita o DXGI é o Microsoft Direct3D 10.

Nas versões anteriores do Direct3D, tarefas de baixo nível, como enumeração de dispositivos de hardware, apresentação de quadros renderizados para uma saída, controle de gama e gerenciamento de uma transição de tela inteira foram incluídas no runtime do Direct3D. Essas tarefas agora são implementadas no DXGI.

A finalidade do DXGI é comunicar-se com o driver do modo kernel e o hardware do sistema, conforme mostrado no diagrama a seguir.

diagrama da comunicação entre aplicativos, dxgi e drivers e hardware

Um aplicativo pode acessar o DXGI diretamente ou chamar as APIs do Direct3D em D3D11_1.h, D3D11.h, D3D10_1.h ou D3D10.h, que manipula as comunicações com o DXGI para você. Talvez você queira trabalhar diretamente com o DXGI se o aplicativo precisar enumerar dispositivos ou controlar como os dados são apresentados a uma saída.

Este tópico inclui as seções a seguir.

Para ver quais formatos têm suporte no hardware do Direct3D 11:

Enumerando adaptadores

Um adaptador é uma abstração do hardware e da funcionalidade de software do seu computador. Geralmente, há muitos adaptadores em seu computador. Alguns dispositivos são implementados em hardware (como o cartão de vídeo) e alguns são implementados no software (como o rasterizador de referência do Direct3D). Os adaptadores implementam a funcionalidade usada por um aplicativo gráfico. O diagrama a seguir mostra um sistema com um único computador, dois adaptadores (placas de vídeo) e três monitores de saída.

diagrama de um computador com duas placas de vídeo e três monitores

Ao enumerar essas partes de hardware, o DXGI cria uma interface IDXGIOutput1 para cada saída (ou monitor) e uma interface IDXGIAdapter2 para cada cartão de vídeo (mesmo que seja um vídeo cartão integrado a uma placa-mãe). A enumeração é feita usando uma chamada de interface IDXGIFactory , IDXGIFactory::EnumAdapters, para retornar um conjunto de interfaces IDXGIAdapter que representam o hardware de vídeo.

O DXGI 1.1 adicionou a interface IDXGIFactory1 . IDXGIFactory1::EnumAdapters1 retorna um conjunto de interfaces IDXGIAdapter1 que representa o hardware de vídeo.

Se você quiser selecionar recursos específicos de hardware de vídeo ao usar APIs Direct3D, recomendamos que você chame iterativamente a função D3D11CreateDevice ou D3D11CreateDeviceAndSwapChain com cada identificador do adaptador e o nível de recurso de hardware possível. Essa função terá êxito se o nível de recurso for compatível com o adaptador especificado.

Novas informações sobre como enumerar adaptadores para Windows 8

Começando com Windows 8, um adaptador chamado "Microsoft Basic Render Driver" está sempre presente. Esse adaptador tem uma VendorId de 0x1414 e um DeviceID de 0x8c. Esse adaptador também tem o valor DXGI_ADAPTER_FLAG_SOFTWARE definido no membro Flags de sua estrutura de DXGI_ADAPTER_DESC2 . Esse adaptador é um dispositivo somente renderização que não tem saídas de exibição. O DXGI nunca retorna DXGI_ERROR_DEVICE_REMOVED para esse adaptador.

Se o driver de exibição de um computador não estiver funcionando ou estiver desabilitado, o adaptador primário (NULL) do computador também poderá ser chamado de "Driver de Renderização Básica da Microsoft". Mas esse adaptador tem saídas e não tem o valor DXGI_ADAPTER_FLAG_SOFTWARE definido. O sistema operacional e os aplicativos usam esse adaptador por padrão. Se um driver de exibição estiver instalado ou habilitado, os aplicativos poderão receber DXGI_ERROR_DEVICE_REMOVED para esse adaptador e, em seguida, devem enumerar novamente os adaptadores.

Quando o adaptador de exibição principal de um computador é o "Adaptador de Exibição Básico da Microsoft" (adaptador WARP ), esse computador também tem um segundo adaptador. Este segundo adaptador é o dispositivo somente renderização que não tem saídas de exibição e para o qual o DXGI nunca retorna DXGI_ERROR_DEVICE_REMOVED.

Se você quiser usar WARP para renderização, computação ou outras tarefas de execução prolongada, recomendamos usar o dispositivo somente renderização. Você pode obter um ponteiro para o dispositivo somente renderização chamando o método IDXGIFactory1::EnumAdapters1 . Você também cria o dispositivo somente renderização quando especifica D3D_DRIVER_TYPE_WARP no parâmetro DriverType de D3D11CreateDevice porque o dispositivo WARP também usa o adaptador WARP somente renderização.

Apresentação

O trabalho do aplicativo é renderizar quadros e solicitar que o DXGI apresente esses quadros à saída. Se o aplicativo tiver dois buffers disponíveis, ele poderá renderizar um buffer enquanto apresenta outro. O aplicativo pode exigir mais de dois buffers, dependendo do tempo necessário para renderizar um quadro ou a taxa de quadros desejada para a apresentação. O conjunto de buffers criados é chamado de cadeia de troca, conforme mostrado aqui.

ilustração de uma cadeia de troca

Uma cadeia de troca tem um buffer frontal e um ou mais buffers traseiros. Cada aplicativo cria sua própria cadeia de troca. Para maximizar a velocidade da apresentação dos dados para uma saída, uma cadeia de troca é quase sempre criada na memória de um subsistema de exibição, que é mostrada na ilustração a seguir.

ilustração de um subsistema de exibição

O subsistema de exibição (que geralmente é um vídeo cartão mas pode ser implementado em uma placa-mãe) contém uma GPU, um DAC (conversor digital para analógico) e memória. A cadeia de troca é alocada dentro dessa memória para tornar a apresentação muito rápida. O subsistema de exibição apresenta os dados no buffer frontal para a saída.

Uma cadeia de troca é configurada para desenhar no modo de tela inteira ou em janelas, o que elimina a necessidade de saber se uma saída é janela ou tela inteira. Uma cadeia de troca de modo de tela inteira pode otimizar o desempenho alternando a resolução de exibição.

Criar uma cadeia de troca

Diferenças entre Direct3D 9 e Direct3D 10: Direct3D 10 é o primeiro componente gráfico a usar DXGI. O DXGI tem alguns comportamentos de cadeia de troca diferentes.
  • No DXGI, uma cadeia de troca é vinculada a uma janela quando a cadeia de troca é criada. Essa alteração melhora o desempenho e salva a memória. As versões anteriores do Direct3D permitiram que a cadeia de troca alterasse a janela à qual a cadeia de troca está vinculada.
  • No DXGI, uma cadeia de troca está vinculada a um dispositivo de renderização na criação. O objeto de dispositivo retornado pelas funções de dispositivo de criação do Direct3D implementa a interface IUnknown . Você pode chamar QueryInterface para consultar a interface IDXGIDevice2 correspondente do dispositivo. Uma alteração no dispositivo de renderização requer que a cadeia de troca seja recriada.
  • No DXGI, os efeitos de troca disponíveis são DXGI_SWAP_EFFECT_DISCARD e DXGI_SWAP_EFFECT_SEQUENTIAL. A partir Windows 8 o efeito de troca de DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL também está disponível. A tabela a seguir mostra um mapeamento do efeito de troca Direct3D 9 para DXGI definido.

    Efeito de troca D3D9 Efeito de troca DXGI
    D3DSWAPEFFECT_DISCARD DXGI_SWAP_EFFECT_DISCARD
    D3DSWAPEFFECT_COPY DXGI_SWAP_EFFECT_SEQUENTIAL com 1 buffer
    D3DSWAPEFFECT_FLIP DXGI_SWAP_EFFECT_SEQUENTIAL com 2 ou mais buffers
    D3DSWAPEFFECT_FLIPEX DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL com 2 ou mais buffers

Os buffers de uma cadeia de troca são criados em um tamanho específico e em um formato específico. O aplicativo especifica esses valores (ou você pode herdar o tamanho da janela de destino) na inicialização e, opcionalmente, pode modificá-los conforme o tamanho da janela muda em resposta a eventos de entrada ou programa do usuário.

Depois de criar a cadeia de troca, você normalmente desejará renderizar imagens nela. Aqui está um fragmento de código que configura um contexto direct3D para renderizar em uma cadeia de troca. Esse código extrai um buffer da cadeia de troca, cria uma exibição de destino de renderização desse buffer e o define no 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);
        

Depois que o aplicativo renderizar um quadro em um buffer de cadeia de troca, chame IDXGISwapChain1::P resent1. Em seguida, o aplicativo pode renderizar a próxima imagem.

Cuidado e alimentação da cadeia de troca

Depois de renderizar sua imagem, chame IDXGISwapChain1::P resent1 e vá renderizar a próxima imagem. Essa é a extensão de sua responsabilidade.

Se você tiver chamado anteriormente IDXGIFactory::MakeWindowAssociation, o usuário poderá pressionar a combinação de teclas Alt-Enter e o DXGI fará a transição do aplicativo entre o modo de tela inteira e janela. IDXGIFactory::MakeWindowAssociation é recomendado, pois um mecanismo de controle padrão para o usuário é altamente desejado.

Embora você não precise escrever mais código do que foi descrito, algumas etapas simples podem tornar seu aplicativo mais responsivo. A consideração mais importante é o redimensionamento dos buffers da cadeia de troca em resposta ao redimensionamento da janela de saída. Naturalmente, a melhor rota do aplicativo é responder a WM_SIZE e chamar IDXGISwapChain::ResizeBuffers, passando o tamanho contido nos parâmetros da mensagem. Esse comportamento obviamente faz com que seu aplicativo responda bem ao usuário quando ele arrasta as bordas da janela, mas também é exatamente o que permite uma transição suave em tela inteira. Sua janela receberá uma mensagem WM_SIZE sempre que essa transição ocorrer e chamar IDXGISwapChain::ResizeBuffers é a chance da cadeia de troca de alocar novamente o armazenamento dos buffers para a apresentação ideal. É por isso que o aplicativo é necessário para liberar todas as referências que ele tem nos buffers existentes antes de chamar IDXGISwapChain::ResizeBuffers.

A falha ao chamar IDXGISwapChain::ResizeBuffers em resposta à mudança para o modo de tela inteira (mais naturalmente, em resposta a WM_SIZE), pode impedir a otimização da inversão, em que o DXGI pode simplesmente trocar qual buffer está sendo exibido, em vez de copiar o valor de uma tela inteira de dados ao redor.

IDXGISwapChain1::P resent1 informará se a janela de saída é totalmente ocluída por meio de DXGI_STATUS_OCCLUDED. Quando isso ocorre, recomendamos que seu aplicativo entre em modo de espera (chamando IDXGISwapChain1::P resent1 com DXGI_PRESENT_TEST), pois os recursos usados para renderizar o quadro são desperdiçados. Usar DXGI_PRESENT_TEST impedirá que todos os dados sejam apresentados enquanto ainda executa a marcar de oclusão. Depois que IDXGISwapChain1::P resent1 retornar S_OK, você deverá sair do modo de espera; não use o código de retorno para alternar para o modo de espera, pois isso pode deixar a cadeia de troca incapaz de abrir mão do modo de tela inteira.

O runtime do Direct3D 11.1, que está disponível a partir do Windows 8, fornece uma cadeia de troca de modelo flip (ou seja, uma cadeia de troca que tem o valor DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL definido no membro SwapEffect de DXGI_SWAP_CHAIN_DESC ou DXGI_SWAP_CHAIN_DESC1). Quando você apresenta quadros a uma saída com uma cadeia de troca de modelo de inversão, o DXGI desvinca o buffer traseiro de todos os locais de estado do pipeline, como um destino de renderização de fusão de saída, que grava no buffer 0 de volta. Portanto, recomendamos que você chame ID3D11DeviceContext::OMSetRenderTargets imediatamente antes de renderizar para o buffer traseiro. Por exemplo, não chame OMSetRenderTargets e execute um trabalho de sombreador de computação que não acabe renderizando para o recurso. Para obter mais informações sobre cadeias de troca de modelo flip e seus benefícios, consulte DXGI Flip Model.

Observação

No Direct3D 10 e no Direct3D 11, você não precisa chamar IDXGISwapChain::GetBuffer para recuperar o buffer de volta 0 depois de chamar IDXGISwapChain1::P resent1 porque, para conveniência, as identidades dos buffers traseiros são alteradas. Isso não acontece no Direct3D 12 e seu aplicativo deve, em vez disso, acompanhar manualmente os índices de buffer de volta.

Redimensionamento de janela de tratamento

Você pode usar o método IDXGISwapChain::ResizeBuffers para lidar com o redimensionamento da janela. Antes de chamar ResizeBuffers, você deve liberar todas as referências pendentes para os buffers da cadeia de troca. O objeto que normalmente contém uma referência ao buffer de uma cadeia de troca é um render-target-view.

O código de exemplo a seguir mostra como chamar ResizeBuffers de dentro do manipulador WindowProc para mensagens WM_SIZE:

    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;

Escolhendo a saída e o tamanho do DXGI

Por padrão, o DXGI escolhe a saída que contém a maior parte da área do cliente da janela. Essa é a única opção disponível para DXGI quando ele se torna tela inteira em resposta ao alt-enter. Se o aplicativo optar por ir para o modo de tela inteira sozinho, ele poderá chamar IDXGISwapChain::SetFullscreenState e passar um IDXGIOutput1 explícito (ou NULL, se o aplicativo estiver feliz em permitir que o DXGI decida).

Para redimensionar a saída enquanto estiver em tela inteira ou em janelas, recomendamos chamar IDXGISwapChain::ResizeTarget, pois esse método redimensiona também a janela de destino. Como a janela de destino é redimensionada, o sistema operacional envia WM_SIZE e seu código chamará naturalmente IDXGISwapChain::ResizeBuffers em resposta. Portanto, é um desperdício de esforço redimensionar seus buffers e, posteriormente, redimensionar o destino.

Depuração no modo de tela inteira

Uma cadeia de troca DXGI abre mão do modo de tela inteira somente quando absolutamente necessário. Isso significa que você pode depurar um aplicativo de tela inteira usando vários monitores, desde que a janela de depuração não se sobreponha à janela de destino da cadeia de troca. Como alternativa, você pode impedir que o modo alterne completamente não definindo o sinalizador DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH .

Se a alternância de modo for permitida, uma cadeia de troca abrirá mão do modo de tela inteira sempre que sua janela de saída for ocluída por outra janela. O marcar de oclusão é executado durante IDXGISwapChain1::P resent1 ou por um thread separado cuja finalidade é watch para ver se o aplicativo ficou sem resposta (e não chama mais IDXGISwapChain1::P resent1). Para desabilitar a capacidade do thread separado de causar uma opção, defina a chave do Registro a seguir como qualquer valor diferente de zero.

HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog

Destruindo uma cadeia de troca

Você pode não liberar uma cadeia de troca no modo de tela inteira porque isso pode criar uma contenção de thread (o que fará com que o DXGI gere uma exceção não contínua). Antes de lançar uma cadeia de troca, primeiro alterne para o modo em janela (usando IDXGISwapChain::SetFullscreenState( FALSE, NULL )) e, em seguida, chame IUnknown::Release.

Usando um monitor girado

Um aplicativo não precisa se preocupar com a orientação do monitor, o DXGI girará um buffer de cadeia de troca durante a apresentação, se necessário. É claro que essa rotação adicional pode afetar o desempenho. Para obter o melhor desempenho, cuide da rotação em seu aplicativo fazendo o seguinte:

  • Use o DXGI_SWAP_CHAIN_FLAG_NONPREROTATED. Isso notifica o DXGI de que o aplicativo produzirá uma imagem girada (por exemplo, alterando sua matriz de projeção). Uma coisa a observar, esse sinalizador só é válido enquanto estiver no modo de tela inteira.
  • Aloque cada buffer de cadeia de troca em seu tamanho girado. Use IDXGIOutput::GetDesc para obter esses valores, se necessário.

Ao executar a rotação em seu aplicativo, o DXGI simplesmente fará uma cópia em vez de uma cópia e uma rotação.

O runtime do Direct3D 11.1, que está disponível a partir do Windows 8, fornece uma cadeia de troca de modelo flip (ou seja, uma cadeia de troca que tem o valor DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL definido no membro SwapEffect de DXGI_SWAP_CHAIN_DESC1). Para maximizar as otimizações de apresentação disponíveis com uma cadeia de troca de modelo de inversão, recomendamos que você faça com que seus aplicativos orientem o conteúdo para corresponder à saída específica na qual o conteúdo reside quando esse conteúdo ocupa totalmente a saída. Para obter mais informações sobre cadeias de troca de modelo flip e seus benefícios, consulte DXGI Flip Model.

Alternando os modos

A cadeia de troca DXGI pode alterar o modo de exibição de uma saída ao fazer uma transição de tela inteira. Para habilitar a alteração automática do modo de exibição, você deve especificar DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH na descrição da cadeia de troca. Se o modo de exibição for alterado automaticamente, o DXGI escolherá o modo mais modesto (o tamanho e a resolução não serão alterados, mas a profundidade da cor poderá). Redimensionar buffers de cadeia de troca não causará uma opção de modo. A cadeia de troca faz uma promessa implícita de que, se você escolher um buffer de fundo que corresponda exatamente a um modo de exibição compatível com a saída de destino, ele mudará para esse modo de exibição ao entrar no modo de tela inteira nessa saída. Consequentemente, você escolhe um modo de exibição escolhendo o tamanho e o formato do buffer traseiro.

Dica de desempenho em tela inteira

Quando você chama IDXGISwapChain1::P resent1 em um aplicativo de tela inteira, a cadeia de troca inverte (em vez de blits) o conteúdo do buffer traseiro para o buffer frontal. Isso requer que a cadeia de troca tenha sido criada usando um modo de exibição enumerado (especificado em DXGI_SWAP_CHAIN_DESC1). Se você não conseguir enumerar modos de exibição ou especificar incorretamente o modo de exibição na descrição, a cadeia de troca poderá executar uma transferência de bloco de bits (bitblt). O bitblt causa uma cópia de alongamento extra, bem como algum aumento no uso de memória de vídeo e é difícil de detectar. Para evitar esse problema, enumere os modos de exibição e inicialize a descrição da cadeia de troca corretamente antes de criar a cadeia de troca. Isso garantirá o desempenho máximo ao inverter no modo de tela inteira e evitar a sobrecarga de memória extra.

Considerações multithread

Ao usar o DXGI em um aplicativo com vários threads, você precisa ter cuidado para evitar a criação de um deadlock, em que dois threads diferentes estão esperando um pelo outro para serem concluídos. Há duas situações em que isso pode ocorrer.

  • O thread de renderização não é o thread de bomba de mensagem.
  • O thread que executa uma API DXGI não é o mesmo thread que criou a janela.

Tenha cuidado para nunca ter a espera de thread de bomba de mensagem no thread de renderização ao usar cadeias de troca de tela inteira. Por exemplo, chamar IDXGISwapChain1::P resent1 (do thread de renderização) pode fazer com que o thread de renderização aguarde no thread de bomba de mensagem. Quando ocorre uma alteração de modo, esse cenário é possível se o Present1 chamar ::SetWindowPos() ou ::SetWindowStyle() e qualquer um desses métodos chamar ::SendMessage(). Nesse cenário, se o thread de bomba de mensagem tiver uma seção crítica que o protege ou se o thread de renderização estiver bloqueado, os dois threads ficarão em deadlock.

Para obter mais informações sobre como usar DXGI com vários threads, consulte Multithreading e DXGI.

Respostas DXGI de DLLMain

Como uma função DllMain não pode garantir a ordem na qual carrega e descarrega DLLs, recomendamos que a função DllMain do aplicativo não chame funções ou métodos Direct3D ou DXGI, incluindo funções ou métodos que criam ou liberam objetos. Se a função DllMain do aplicativo chamar um componente específico, esse componente poderá chamar outra DLL que não esteja presente no sistema operacional, o que faz com que o sistema operacional falhe. Direct3D e DXGI podem carregar um conjunto de DLLs, normalmente um conjunto de drivers, que difere de computador para computador. Portanto, mesmo que seu aplicativo não falhe em seus computadores de desenvolvimento e teste quando sua função DllMain chamar funções ou métodos Direct3D ou DXGI, ele poderá falhar quando ele for executado em outro computador.

Para impedir que você crie um aplicativo que possa causar falha no sistema operacional, o DXGI fornece as seguintes respostas nas situações especificadas:

  • Se a função DllMain do aplicativo liberar sua última referência a uma fábrica DXGI, o DXGI gerará uma exceção.
  • Se a função DllMain do aplicativo criar uma fábrica DXGI, dXGI retornará um código de erro.

Alterações do DXGI 1.1

Adicionamos a seguinte funcionalidade no DXGI 1.1.

Alterações do DXGI 1.2

Adicionamos a seguinte funcionalidade no DXGI 1.2.

  • Cadeia de troca estéreo
  • Cadeia de troca de modelo de inversão
  • Apresentação otimizada (rolagem, retângulos sujo e rotação)
  • Recursos e sincronização compartilhados aprimorados
  • Duplicação da Área de Trabalho
  • Uso otimizado da memória de vídeo
  • Suporte para formatos de 16 bits por pixel (bpp) (DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM DXGI_FORMAT_B4G4R4A4_UNORM)
  • APIs de depuração

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

Guia de programação para DXGI