Direct3D 11에서 디바이스 제거 시나리오 처리

이 항목에서는 그래픽 어댑터가 제거되거나 다시 초기화될 때 Direct3D 및 DXGI 디바이스 인터페이스 체인을 다시 만드는 방법을 설명합니다.

DirectX 9에서 애플리케이션은 D3D 디바이스가 비운전 상태로 들어가는 "디바이스 손실" 상태가 발생할 수 있습니다. 예를 들어 전체 화면 Direct3D 9 애플리케이션이 포커스를 잃으면 Direct3D 디바이스는 "손실됨"이 되고 분실된 디바이스로 그리려는 시도는 자동으로 실패합니다. Direct3D 11은 가상 그래픽 디바이스 인터페이스를 사용하여 여러 프로그램에서 동일한 물리적 그래픽 디바이스를 공유할 수 있도록 하고 앱이 Direct3D 디바이스를 제어하지 않는 조건을 제거합니다. 그러나 그래픽 어댑터 가용성이 변경되는 것은 여전히 가능합니다. 예시:

  • 그래픽 드라이버가 업그레이드되었습니다.
  • 시스템이 절전 그래픽 어댑터에서 성능 그래픽 어댑터로 변경됩니다.
  • 그래픽 디바이스가 응답을 중지하고 다시 설정됩니다.
  • 그래픽 어댑터가 물리적으로 연결되거나 제거됩니다.

이러한 상황이 발생하면 DXGI는 Direct3D 디바이스를 다시 초기화하고 디바이스 리소스를 다시 만들어야 함을 나타내는 오류 코드를 반환합니다. 이 연습에서는 Direct3D 11 앱과 게임이 그래픽 어댑터가 다시 설정, 제거 또는 변경되는 모든 상황을 감지하고 대응하는 방법을 설명합니다. 코드 예제는 Microsoft Visual Studio 2015와 함께 제공되는 DirectX 11 앱(유니버설 Windows) 템플릿에서 제공됩니다.

지침

1단계:

렌더링 루프에 디바이스 제거 오류에 대한 검사를 포함합니다. IDXGISwapChain::Present(또는 Present1 등)를 호출하여 프레임을 표시합니다. 그런 다음, DXGI\_ERROR\_DEVICE\_REMOVED 또는 DXGI\_ERROR\_DEVICE\_RESET이 반환되었는지를 확인합니다.

먼저 템플릿은 DXGI 스왑 체인에서 반환된 HRESULT를 저장합니다.

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

프레임을 표시하기 위한 다른 모든 작업을 처리한 후 템플릿은 디바이스에 대한 검사 제거된 오류를 표시합니다. 필요한 경우 디바이스 제거 조건을 처리하는 메서드를 호출합니다.

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

2단계:

또한 창 크기 변경에 응답할 때 디바이스 제거 오류에 대한 검사 포함합니다. 이곳은 여러 가지 이유로 DXGI\_ERROR\_DEVICE\_REMOVED 또는 DXGI\_ERROR\_DEVICE\_RESET를 검사하기에 좋은 위치입니다.

  • 스왑 체인의 크기를 조정하려면 디바이스 제거 오류를 반환할 수 있는 기본 DXGI 어댑터를 호출해야 합니다.
  • 앱이 다른 그래픽 디바이스에 연결된 모니터로 이동했을 수 있습니다.
  • 그래픽 장치를 제거하거나 다시 설정하면 데스크톱 해상도가 변경되어 창 크기가 변경되는 경우가 많습니다.

템플릿은 ResizeBuffers에서 반환된 HRESULT를 검사합니다.

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

3단계:

앱에서 DXGI_ERROR_DEVICE_REMOVED 오류가 발생할 때마다 Direct3D 디바이스가 다시 초기화되고 모든 디바이스 종속 리소스가 다시 생성되어야 합니다. 이전 Direct3D 디바이스로 만든 그래픽 디바이스 리소스에 대한 참조를 해제합니다. 이제 해당 리소스가 유효하지 않으며 스왑 체인에 대한 모든 참조를 해제해야 새 리소스를 만들 수 있습니다.

HandleDeviceLost 메서드는 스왑 체인을 해제하고 앱 구성 요소에 디바이스 리소스를 해제하도록 알릴 수 있습니다.

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

그런 다음, 새 스왑 체인을 만들고 디바이스 관리 클래스에서 제어하는 디바이스 종속 리소스를 다시 초기화합니다.

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

디바이스 및 스왑 체인이 다시 설정되면 앱 구성 요소에 디바이스 종속 리소스를 다시 초기화하도록 알 수 있습니다.

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

HandleDeviceLost 메서드가 종료되면 컨트롤은 렌더링 루프로 돌아갑니다. 그러면 다음 프레임을 계속 그립니다.

설명

디바이스 제거 오류의 원인 조사

DXGI 디바이스 제거 오류와 관련된 반복 문제는 그래픽 코드가 그리기 루틴 중에 잘못된 조건을 만들고 있음을 나타낼 수 있습니다. 그래픽 드라이버의 하드웨어 오류 또는 버그를 나타낼 수도 있습니다. 디바이스 제거 오류의 원인을 조사하려면 Direct3D 디바이스를 해제하기 전에 ID3D11Device::GetDeviceRemovedReason을 호출합니다. 이 메서드는 디바이스 제거 오류의 원인을 나타내는 6가지 가능한 DXGI 오류 코드 중 하나를 반환합니다.

  • DXGI_ERROR_DEVICE_HUNG: 앱에서 보낸 그래픽 명령의 조합이 잘못되어 그래픽 드라이버의 응답이 중지되었습니다. 이 오류가 반복적으로 발생하는 경우 앱에서 디바이스가 중단되고 디버깅이 필요했음을 나타낼 수 있습니다.
  • DXGI_ERROR_DEVICE_REMOVED: 그래픽 디바이스를 물리적으로 제거 또는 껐거나, 드라이버가 업그레이드되었습니다. 이는 경우에 따라 발생하며 정상입니다. 앱 또는 게임은 이 항목에 설명된 대로 디바이스 리소스를 다시 만들어야 합니다.
  • DXGI_ERROR_DEVICE_RESET: 잘못된 형식의 명령 때문에 그래픽 디바이스가 실패했습니다. 이 오류가 반복적으로 발생하면 코드에서 잘못된 그리기 명령을 보내는 것일 수 있습니다.
  • DXGI_ERROR_DRIVER_INTERNAL_ERROR: 그래픽 드라이버에 오류가 발생하여 디바이스가 다시 설정되었습니다.
  • DXGI_ERROR_INVALID_CALL: 애플리케이션이 잘못된 매개 변수 데이터를 제공했습니다. 이 오류가 한 번만 발생하는 경우 코드로 인해 디바이스가 제거된 조건이 발생했으며 디버그해야 합니다.
  • S_OK: 현재 그래픽 디바이스를 무효화하지 않은 채 그래픽 디바이스를 활성화, 비활성화 또는 재설정할 때 반환됩니다. 예를 들어 앱이 WARP(Windows Advanced Rasterization Platform)를 사용하고 하드웨어 어댑터를 사용할 수 있게 되면 이 오류 코드를 반환할 수 있습니다.

다음 코드는 DXGI_ERROR_DEVICE_REMOVED 오류 코드를 검색하고 이를 디버그 콘솔에 출력합니다. 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

자세한 내용은 GetDeviceRemovedReasonDXGI_ERROR를 참조하세요.

디바이스 제거 처리 테스트

Visual Studio의 개발자 명령 프롬프트는 Visual Studio 그래픽 진단과 관련된 Direct3D 이벤트 캡처 및 재생을 위한 명령줄 도구 'dxcap'을 지원합니다. 앱이 실행되는 동안 명령줄 옵션 "-forcetdr"을 사용하여 GPU 시간 제한 검색 및 복구 이벤트를 강제로 발생시킬 수 있으므로 DXGI_ERROR_DEVICE_REMOVED를 트리거하고 오류 처리 코드를 테스트할 수 있습니다.

참고 DXCap 및 해당 지원 DLL은 Windows SDK를 통해 더 이상 배포되지 않는 Windows 10용 그래픽 도구의 일부로 system32/syswow64에 설치됩니다. 대신 선택적 OS 구성 요소인 주문형 그래픽 도구 기능을 통해 제공되며 Windows 10에서 그래픽 도구를 사용하도록 설정하고 사용하려면 설치해야 합니다. Windows 10에 대한 그래픽 도구를 설치하는 방법에 대한 자세한 내용은 다음을 참조하세요. https://msdn.microsoft.com/library/mt125501.aspx#InstallGraphicsTools