Handling device removed scenarios in Direct3D 11
[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]
This topic explains how to recreate the Direct3D and DXGI device interface chain when the graphics adapter is removed or reinitialized.
In DirectX 9, applications could encounter a "device lost" condition where the D3D device enters a non-operational state. For example, when a full-screen Direct3D 9 application loses focus the Direct3D device becomes "lost;" any attempts to draw with a lost device will silently fail. Direct3D 11 uses virtual graphics device interfaces, enabling multiple programs to share the same physical graphics device and eliminating conditions where apps lose control of the Direct3D device. However, it is still possible for graphics adapter availability to change. For example:
- The graphics driver is upgraded.
- The system changes from a power-saving graphics adapter to a performance graphics adapter.
- The graphics device stops responding and is reset.
- A graphics adapter is physically attached or removed.
When such circumstances arise, DXGI returns an error code indicating that the Direct3D device must be reinitialized and device resources must be recreated. This walkthrough explains how Direct3D 11 apps and games can detect and respond to any circumstance where the graphics adapter is reset, removed, or changed. Code examples are provided from the Direct3D Windows Store app templates provided with Microsoft Visual Studio 2013.
Instructions
Step 1:
Include a check for the device removed error in the rendering loop. Present the frame by calling IDXGISwapChain::Present (or Present1, and so on). Then check whether it returned DXGI_ERROR_DEVICE_REMOVED.
First, the template stores the HRESULT returned by the DXGI swap chain:
HRESULT hr = m_swapChain->Present(1, 0);
After taking care of all other work for presenting the frame, the template checks for the device removed error. If necessary, it calls a method to handle the device removed condition:
// 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)
{
HandleDeviceLost();
}
else
{
DX::ThrowIfFailed(hr);
}
Step 2:
Also include a check for the device removed error when responding to window size changes. This is a good place to check for DXGI_ERROR_DEVICE_REMOVED for several reasons:
- Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
- The app might have moved to a monitor that's attached to a different graphics device.
- When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change for full-screen desktop apps and Windows Store apps.
The template checks the HRESULT returned by 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)
{
// 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);
}
Step 3:
Any time your app receives the DXGI_ERROR_DEVICE_REMOVED error, it must reinitialize the Direct3D device and reinitialize any device-dependent resources. Release any references to graphics device resources created with the previous Direct3D device; those resources are now invalid, and all references to the swap chain must be released before a new one can be created.
The HandleDeviceLost method releases the swap chain and notifies app components to release device resources:
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();
}
Then it creates a new swap chain and reinitializes the device-dependent resources controlled by the device management class:
// Create the new device and swap chain.
CreateDeviceResources();
m_d2dContext->SetDpi(m_dpi, m_dpi);
CreateWindowSizeDependentResources();
After the device and swap chain have been re-established, it notifies app components to reinitialize device-dependent resources:
// 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();
}
When the HandleDeviceLost method exits, control returns to the rendering loop, which continues on to draw the next frame.
Remarks
Investigating the cause of device removed errors
Repeat issues with DXGI device removed errors can indicate that your graphics code is creating invalid conditions during a drawing routine. It can also indicate a hardware failure or a bug in the graphics driver. To investigate the cause of device removed errors, call ID3D11Device::GetDeviceRemovedReason before releasing the Direct3D device. This method returns one of six possible DXGI error codes indicating the reason for the device removed error:
- DXGI_ERROR_DEVICE_HUNG: The graphics driver stopped responding because of an invalid combination of graphics commands sent by the app. If you get this error repeatedly, it is a likely indication that your app caused the device to hang and needs to be debugged.
- DXGI_ERROR_DEVICE_REMOVED: The graphics device has been physically removed, turned off, or a driver upgrade has occurred. This happens occasionally and is normal; your app or game should recreate device resources as described in this topic.
- DXGI_ERROR_DEVICE_RESET: The graphics device failed because of a badly formed command. If you get this error repeatedly it may mean that your code is sending invalid drawing commands.
- DXGI_ERROR_DRIVER_INTERNAL_ERROR: The graphics driver encountered an error and reset the device.
- DXGI_ERROR_INVALID_CALL: The application provided invalid parameter data. If you get this error even once, it means that your code caused the device removed condition and must be debugged.
- S_OK: Returned when a graphics device was enabled, disabled, or reset without invalidating the current graphics device. For example, this error code can be returned if an app is using Windows Advanced Rasterization Platform (WARP) and a hardware adapter becomes available.
The following code will retrieve the DXGI_ERROR_DEVICE_REMOVED error code and print it to the debug console. Insert this code at the beginning of the HandleDeviceLost method:
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
See GetDeviceRemovedReason and DXGI_ERROR for more details.