次の方法で共有


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: アプリケーションが無効なパラメーター データを提供しました。 このエラーが 1 回でも発生する場合は、コードによってデバイスが削除された状態が発生し、デバッグする必要があることを意味します。
  • S_OK: 現在のグラフィックス デバイスを無効にせずに、グラフィックス デバイスが有効、無効、またはリセットされたときに返されます。 たとえば、アプリが Windows Advanced Rasterization Platform (WARP) を使用していて、ハードウェア アダプターが使用可能になった場合、このエラー コードを返すことができます。

次のコードは、 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