Обработка сценариев удаления устройства в Direct3D 11

В этом разделе объясняется, как повторно создать цепочку интерфейса устройства Direct3D и DXGI при удалении или повторной инициализации графического адаптера.

В приложениях DirectX 9 может возникать условие потери устройства, при котором устройство D3D переходит в нерабочее состояние. Например, когда полноэкранное приложение Direct3D 9 теряет фокус, устройство Direct3D "теряется" и любые попытки использования потерянного устройства завершаются ошибкой. В Direct3D 11 используются виртуальные интерфейсы графических устройств, что позволяет нескольким программам одновременно использовать одно и то же физическое графическое устройство и исключает возможность появления условий, при которых приложения теряют контроль над устройством Direct3D. Однако доступность графического адаптера все же может измениться. Например:

  • Выполняется обновление графического драйвера.
  • Система переходит с энергосберегающего графического адаптера на производительный графический адаптер.
  • Графическое устройство перестает отвечать и сбрасывается.
  • Физическое подключение или удаление графического адаптера.

При возникновении таких условий DXGI возвращает код ошибки, указывающий, что следует повторно инициализировать устройство Direct3D и воссоздать ресурсы устройства. В этом пошаговом руководстве объясняется, как приложения и игры Direct3D 11 могут обнаружить условия, при которых графический адаптер сбрасывается, удаляется или изменяется, и отреагировать соответствующим образом. Примеры кода взяты из шаблонов универсальных приложений Windows на Direct3D, предоставленных в Microsoft Visual Studio 2013.

Инструкции

Этап 1:

Включите проверку ошибки удаленного устройства в цикле отрисовки. Представьте кадр, вызвав метод IDXGISwapChain::Present (или метод Present1 и т. д.). Затем проверьте, возвратил ли метод ошибку DXGI_ERROR_DEVICE_REMOVED.

Прежде всего, шаблон сохраняет HRESULT, возвращенный цепочкой буферов DXGI.



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)
{
    HandleDeviceLost();
}
else
{
    DX::ThrowIfFailed(hr);
}

Этап 2:

Также включите проверку на наличие ошибки удаленного устройства в ответ на изменения размера окна. На этом этапе рекомендуется проверять DXGI_ERROR_DEVICE_REMOVED по нескольким причинам.

  • Для изменения размера цепочки буферов требуется вызвать базовый адаптер DXGI, который может возвратить ошибку удаленного устройства.
  • Приложение может теперь отображаться на мониторе, подключенном к другому графическому устройству.
  • При удалении или сбросе графического устройства разрешение рабочего стола часто меняется, в результате чего меняется размер окна полноэкранных классических приложений и универсальных приложений Windows.

Шаблон проверяет HRESULT, возвращенный методом 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);
}

Этап 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 может означать, что ваш графический код создает недопустимые условия в процессе отрисовки. Также это может указывать на сбой оборудования или ошибку в графическом драйвере. Чтобы исследовать причину ошибок удаленного устройства, вызовите метод ID3D11Device::GetDeviceRemovedReason до освобождения устройства Direct3D. Этот метод возвратит один из шести возможных кодов ошибок 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 и аппаратный адаптер становится доступен.

В следующем коде будет получен код ошибки 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 и DXGI_ERROR.

 

 

Показ:
© 2015 Microsoft