处理 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_REMOVEDDXGI_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

有关详细信息,请参阅 GetDeviceRemovedReasonDXGI_ERROR

测试设备移除后的处理

Visual Studio 的开发人员命令提示符支持与 Visual Studio 图形诊断相关的 Direct3D 事件捕获和播放的命令行工具“dxcap”。 可以在应用运行时使用命令行选项“-forcetdr”,这将强制启动 GPU 超时检测和恢复事件,从而触发 DXGI_ERROR_DEVICE_REMOVED,并允许您测试错误处理代码。

Note 作为 Windows 10 图形工具的一部分,DXCap 及其支持 DLL 安装到 system32/syswow64 中,而这些工具将不再通过 Windows SDK 分发。 而是通过按需图形工具功能提供,这是可选的 OS 组件,必须安装它们才能在 Windows 10 上启用和使用图形工具。 有关如何安装适用于 Windows 10 的图形工具的详细信息,可在此处找到: https://msdn.microsoft.com/library/mt125501.aspx#InstallGraphicsTools