Compartilhar via


Manipular cenários de remoção de dispositivo no Direct3D 11

Este tópico explica como recriar a cadeia de interface do dispositivo Direct3D e DXGI quando o adaptador gráfico for removido ou reinicializado.

No DirectX 9, os aplicativos podem encontrar uma condição "dispositivo perdido", no qual o dispositivo D3D entra em um estado não operacional. Por exemplo, quando um aplicativo Direct3D 9 de tela inteira perde o foco, o dispositivo Direct3D se torna "perdido;" todas as tentativas de desenhar com um dispositivo perdido falharão silenciosamente. O Direct3D 11 usa interfaces de dispositivo de elementos gráficos virtuais, permitindo que vários programas compartilhem o mesmo dispositivo gráfico físico e eliminando condições em que os aplicativos perdem o controle do dispositivo Direct3D. No entanto, ainda é possível que a disponibilidade do adaptador gráfico seja alterada. Por exemplo:

  • O driver gráfico é atualizado.
  • O sistema muda de um adaptador gráfico que salva energia para um adaptador gráfico de desempenho.
  • O dispositivo gráfico para de responder e é redefinido.
  • Um adaptador gráfico é fisicamente anexado ou removido.

Quando essas circunstâncias surgem, o DXGI retorna um código de erro que indica que o dispositivo Direct3D deve ser reinicializado e os recursos do dispositivo devem ser recriados. Este passo a passo explica como aplicativos e jogos do Direct3D 11 podem detectar e responder a qualquer circunstância em que o adaptador gráfico seja redefinido, removido ou alterado. Exemplos de código são fornecidos a partir do modelo de Aplicativo DirectX 11 (Universal do Windows) fornecido com o Microsoft Visual Studio 2015.

Instruções

Etapa 1:

Inclua uma verificação para o erro de dispositivo removido no loop de renderização. Apresente o quadro chamando IDXGISwapChain::Present (ou Present1e assim por diante). Em seguida, verifique se ele retornou DXGI_ERROR_DEVICE_REMOVED ou DXGI_ERROR_DEVICE_RESET.

Primeiro, o modelo armazena o HRESULT retornado pela cadeia de troca DXGI:

HRESULT hr = m_swapChain->Present(1, 0);

Depois de cuidar de todos os outros trabalhos para apresentar o quadro, o modelo verifica se o dispositivo removeu o erro. Se necessário, ele chama um método para lidar com a condição de dispositivo removido.

// If the device was removed either by a disconnection or a driver upgrade, we
// must recreate all device resources.
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
    HandleDeviceLost();
}
else
{
    DX::ThrowIfFailed(hr);
}

Etapa 2:

Além disso, inclua uma verificação do erro de dispositivo removido quando responder às mudanças no tamanho da janela. Este é um bom lugar para verificar DXGI_ERROR_DEVICE_REMOVED ou DXGI_ERROR_DEVICE_RESET por vários motivos:

  • Redimensionar a swap chain requer uma chamada para o adaptador DXGI subjacente, que pode retornar o erro de dispositivo removido.
  • O aplicativo pode ter sido movido para um monitor anexado a um dispositivo gráfico diferente.
  • Quando um dispositivo gráfico é removido ou redefinido, a resolução da área de trabalho geralmente é alterada, resultando em uma alteração de tamanho de janela.

O modelo verifica o HRESULT retornado por ResizeBuffers:

// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
    2, // Double-buffered swap chain.
    static_cast<UINT>(m_d3dRenderTargetSize.Width),
    static_cast<UINT>(m_d3dRenderTargetSize.Height),
    DXGI_FORMAT_B8G8R8A8_UNORM,
    0
    );

if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
    // If the device was removed for any reason, a new device and swap chain will need to be created.
    HandleDeviceLost();

    // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method 
    // and correctly set up the new device.
    return;
}
else
{
    DX::ThrowIfFailed(hr);
}

Passo 3:

Sempre que seu aplicativo recebe o erro DXGI_ERROR_DEVICE_REMOVED, ele deve reinicializar o dispositivo Direct3D e recriar todos os recursos dependentes do dispositivo. Libere todas as referências aos recursos de dispositivo gráfico criados com o dispositivo Direct3D anterior; esses recursos agora são inválidos e todas as referências à cadeia de troca devem ser liberadas antes que uma nova possa ser criada.

O método HandleDeviceLost libera a cadeia de troca e notifica os componentes do aplicativo para liberar recursos do dispositivo:

m_swapChain = nullptr;

if (m_deviceNotify != nullptr)
{
    // Notify the renderers that device resources need to be released.
    // This ensures all references to the existing swap chain are released so that a new one can be created.
    m_deviceNotify->OnDeviceLost();
}

Em seguida, ele cria uma nova cadeia de troca e reinicializa os recursos dependentes do dispositivo controlados pela classe de gerenciamento de dispositivos:

// Create the new device and swap chain.
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();

Depois que o dispositivo e a cadeia de troca forem restabelecidas, ele notificará os componentes do aplicativo para reinicializar os recursos dependentes do dispositivo:

// Create the new device and swap chain.
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();

if (m_deviceNotify != nullptr)
{
    // Notify the renderers that resources can now be created again.
    m_deviceNotify->OnDeviceRestored();
}

Quando o método HandleDeviceLost for encerrado, o controle retornará ao loop de renderização, que continua a desenhar o próximo quadro.

Observações

Investigando a causa dos erros removidos do dispositivo

Problemas repetidos com erros de remoção do dispositivo DXGI podem indicar que, durante uma rotina de desenho, seu código gráfico está criando condições inválidas. Ele também pode indicar uma falha de hardware ou um bug no driver gráfico. Para investigar a causa dos erros de remoção do dispositivo, chame ID3D11Device::GetDeviceRemovedReason antes de liberar o dispositivo Direct3D. Esse método retorna um dos seis possíveis códigos de erro DXGI que indicam o motivo do erro de remoção do dispositivo.

  • DXGI_ERROR_DEVICE_HUNG: O driver de gráficos parou de responder devido a uma combinação inválida de comandos gráficos enviados pelo aplicativo. Se você receber esse erro repetidamente, é uma indicação provável de que seu aplicativo causou o travamento do dispositivo e precisa ser depurado.
  • DXGI_ERROR_DEVICE_REMOVED: o dispositivo gráfico foi fisicamente removido, desativado ou ocorreu uma atualização do driver. Isso acontece ocasionalmente e é normal; seu aplicativo ou jogo deve recriar os recursos do dispositivo, conforme descrito neste tópico.
  • DXGI_ERROR_DEVICE_RESET: O dispositivo gráfico falhou devido a um comando mal formado. Se você receber esse erro repetidamente, isso pode significar que o código está enviando comandos de desenho inválidos.
  • DXGI_ERROR_DRIVER_INTERNAL_ERROR: O driver gráfico encontrou um erro e redefiniu o dispositivo.
  • DXGI_ERROR_INVALID_CALL: o aplicativo forneceu dados de parâmetro inválidos. Se você receber esse erro mesmo uma vez, isso significa que o código causou a condição de remoção do dispositivo e deve ser depurado.
  • S_OK: retornado quando um dispositivo gráfico foi habilitado, desabilitado ou redefinido sem invalidar o dispositivo gráfico atual. Por exemplo, esse código de erro poderá ser retornado se um aplicativo estiver usando a WARP (Plataforma de Rasterização Avançada do Windows) e um adaptador de hardware ficar disponível.

O seguinte código irá recuperar o código de erro DXGI_ERROR_DEVICE_REMOVED e imprimi-lo no console de depuração. Insira este código no início do método HandleDeviceLost:

    HRESULT reason = m_d3dDevice->GetDeviceRemovedReason();

#if defined(_DEBUG)
    wchar_t outString[100];
    size_t size = 100;
    swprintf_s(outString, size, L"Device removed! DXGI_ERROR code: 0x%X\n", reason);
    OutputDebugStringW(outString);
#endif

Para obter mais detalhes, consulte GetDeviceRemovedReason e DXGI_ERROR.

Teste de remoção de manipulação do dispositivo

O Prompt de Comando do Desenvolvedor do Visual Studio dá suporte a uma ferramenta de linha de comando 'dxcap' para captura e reprodução de eventos Direct3D relacionada ao Diagnóstico Gráfico do Visual Studio. Você pode usar a opção de linha de comando "-forcetdr" enquanto seu aplicativo está em execução, o que forçará um evento de Timeout Detection and Recovery de GPU, acionando o DXGI_ERROR_DEVICE_REMOVED e permitindo que você teste o código de tratamento de erros.

Nota O DXCap e suas DLLs de suporte são instalados no system32/syswow64 como parte das Ferramentas Gráficas para Windows 10 que não são mais distribuídas por meio do SDK do Windows. Em vez disso, eles são fornecidos por meio do Recurso de Ferramentas Gráficas sob Demanda que é um componente opcional do sistema operacional e devem ser instalados para habilitar e usar as Ferramentas Gráficas no Windows 10. Mais informações sobre como instalar as Ferramentas Gráficas para Windows 10 podem ser encontradas aqui: https://msdn.microsoft.com/library/mt125501.aspx#InstallGraphicsTools