Direct3D 11 中處理裝置移除的案例

本主題說明如何移除或重新初始化圖形介面卡時,重新建立 Direct3D 與 DXGI 裝置介面鏈。

在 DirectX 9 中,應用程式可能會遇到裝置遺失的情況,其中 D3D 裝置會進入非操作狀態。 例如,當一個全螢幕的 Direct3D 9 應用程式失去焦點時,Direct3D 裝置會變成遺失;任何使用遺失裝置繪製的嘗試都會以無訊息的方式失敗。 Direct3D 11 使用虛擬圖形裝置介面,讓多個程式共用相同的實體圖形裝置,並消除應用程式失去 Direct3D 裝置控制權的狀況。 不過,圖形介面卡的可用性仍有可能改變。 例如:

  • 圖形驅動程式已升級。
  • 系統從省電圖形介面卡變更為高效能圖形介面卡。
  • 圖形裝置停止回應並重設。
  • 圖形介面卡已物理連線或移除。

當發生這種情況時,DXGI 會傳回錯誤碼,指出必須重新初始化 Direct3D 裝置,而且必須重新建立裝置資源。 本演練說明 Direct3D 11 應用程式和遊戲如何偵測圖形介面卡重設、移除或變更的任何情況並做出回應。 程式碼範例來自 Microsoft Visual Studio 2015 隨附的 DirectX 11 App (Universal Windows) 範本。

指示

步驟 1:

包括在呈現環路中對裝置已移除錯誤的檢查。 透過呼叫 IDXGI SwapChain::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。 此方法傳回六個可能的 DXGI 錯誤碼之一,指出裝置移除錯誤的原因:

  • DXGI_ERROR_DEVICE_HUNG:圖形驅動程式停止回應,因為應用程式傳送的圖形命令組合無效。 如果反複發生此錯誤,則可能表示您的應用程式造成裝置掛起,需要進行偵錯。
  • DXGI_ERROR_DEVICE_REMOVED:圖形裝置已被實際移除、關閉或發生驅動程式升級。 這種情況偶爾發生,而且很正常;您的應用程式或遊戲應該如本主題所述重新建立裝置資源。
  • DXGI_ERROR_DEVICE_RESET:圖形裝置因命令格式錯誤而失敗。 如果重複出現此錯誤,則可能表示您的程式碼正在傳送無效的工程圖命令。
  • DXGI_ERROR_DRIVER_INTERNAL_ERROR:圖形驅動程式發生錯誤並重設裝置。
  • DXGI_ERROR_INVALID_CALL:應用程式提供的參數資料無效。 如果即使只出現一次此錯誤,也表示您的程式碼導致裝置被刪除並必須調試。
  • S_OK:啟用、停用或重設圖形裝置時傳回,而不會使目前的圖形裝置失效。 例如,如果應用程式正在使用 Windows 進階點陣化平台 (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

詳細資訊,GetDeviceRemovedReason and DXGI_ERROR。

測試裝置移除的處理

Visual Studio 的開發人員命令提示字元支援與 Visual Studio Graphics Diagnostics 相關的 Direct3D 事件擷取和播放的命令列工具 'dxcap'。 您可以在應用程式執行時使用命令列選項:forcetdr,這會強制 GPU 逾時偵測與復原事件,進而觸發 DXGI_ERROR_DEVICE_REMOVED 並讓您測試錯誤處理程式碼。

備註:DXCap 及其支援 DLL 已安裝在 system32/syswow64 中,作為 Windows 10 圖形工具的一部分,不再透過 Windows SDK 發佈。 而是透過圖形工具功能隨選提供,該功能是選購的作業系統元件,必須安裝才能在 Windows 10 上啟用和使用圖形工具。 如需如何安裝 Windows 10 圖形工具的詳細資訊,請造訪:https://msdn.microsoft.com/library/mt125501.aspx#InstallGraphicsTools