From 9287ec97ebd9c5041bb8eb4e649337b49e9eb25f Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 4 Nov 2017 04:00:00 +1100 Subject: [PATCH] [host] made DXGI ReInitialization more robust on mode changes DXGI doesn't like to restart too fast, com exceptions are logged and the duplication device fails to create with an E_ACCESS_DENIED error. Adding a 200ms sleep between teardown and re-init resolves this issue. --- host/Capture/DXGI.cpp | 64 +++++++++++++++++++++++++++++-------------- host/Capture/DXGI.h | 2 ++ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/host/Capture/DXGI.cpp b/host/Capture/DXGI.cpp index bfe6390a..3cfc3e35 100644 --- a/host/Capture/DXGI.cpp +++ b/host/Capture/DXGI.cpp @@ -118,22 +118,23 @@ bool DXGI::Initialize() ); adapter.Release(); - if (FAILED(status)) + if (FAILED(status) || !m_device) { DEBUG_ERROR("Failed to create D3D11 device"); DeInitialize(); return false; } - CComQIPtr device1 = m_device; - if (!device1) + // we try this twice just incase we still get an error + // on re-initialization + for(int i = 0; i < 2; ++i) { - DEBUG_ERROR("Failed to get IDXGIDevice1"); - DeInitialize(); - return false; + status = m_output->DuplicateOutput(m_device, &m_dup); + if (SUCCEEDED(status)) + break; + Sleep(200); } - status = m_output->DuplicateOutput(m_device, &m_dup); if (FAILED(status)) { DEBUG_ERROR("DuplicateOutput Failed: %08x", status); @@ -181,6 +182,7 @@ void DXGI::DeInitialize() { delete[] m_pointer; m_pointer = NULL; + m_pointerBufSize = 0; } if (m_texture) @@ -204,6 +206,19 @@ void DXGI::DeInitialize() m_initialized = false; } +bool DXGI::ReInitialize() +{ + DeInitialize(); + + /* + DXGI needs some time when mode switches occur, failing to do so causes + failure to start and exceptions internal to DXGI + */ + Sleep(200); + + return Initialize(); +} + FrameType DXGI::GetFrameType() { if (!m_initialized) @@ -230,6 +245,9 @@ size_t DXGI::GetMaxFrameSize() bool DXGI::GrabFrame(FrameInfo & frame) { + if (!m_initialized) + return false; + DXGI_OUTDUPL_FRAME_INFO frameInfo; CComPtr res; @@ -239,27 +257,31 @@ bool DXGI::GrabFrame(FrameInfo & frame) status = m_dup->AcquireNextFrame(INFINITE, &frameInfo, &res); if (SUCCEEDED(status)) break; - - // desktop switch, mode change or switch DWM on or off - if (status == DXGI_ERROR_ACCESS_LOST) - { - DeInitialize(); - if (!Initialize()) + + switch (status) + { + case DXGI_ERROR_ACCESS_LOST: // desktop switch, mode change or switch DWM on or off + case WAIT_ABANDONED: // this can happen also during desktop switches, not documented by MS though { - DEBUG_ERROR("Failed to re-initialize after access was lost"); - return false; + if (!ReInitialize()) + { + DEBUG_ERROR("Failed to re-initialize after access was lost"); + return false; + } + continue; } - continue; - } - // unknown failure - DEBUG_INFO("AcquireNextFrame failed: %08x", status); - return false; + default: + // unknown failure + DEBUG_INFO("AcquireNextFrame failed: %08x", status); + return false; + } } // retry count exceeded if (FAILED(status)) { + m_dup->ReleaseFrame(); DEBUG_ERROR("Failed to acquire next frame"); return false; } @@ -267,6 +289,7 @@ bool DXGI::GrabFrame(FrameInfo & frame) CComQIPtr src = res; if (!src) { + m_dup->ReleaseFrame(); DEBUG_ERROR("Failed to get src ID3D11Texture2D"); return false; } @@ -290,6 +313,7 @@ bool DXGI::GrabFrame(FrameInfo & frame) status = m_dup->GetFramePointerShape(m_pointerBufSize, m_pointer, &m_pointerSize, &m_shapeInfo); if (!SUCCEEDED(status)) { + m_dup->ReleaseFrame(); DEBUG_ERROR("Failed to get the new pointer shape: %08x", status); return false; } diff --git a/host/Capture/DXGI.h b/host/Capture/DXGI.h index f711f91f..f490d0a8 100644 --- a/host/Capture/DXGI.h +++ b/host/Capture/DXGI.h @@ -42,6 +42,8 @@ namespace Capture bool GrabFrame(struct FrameInfo & frame); private: + bool ReInitialize(); + bool m_initialized; MTMemcpy m_memcpy; unsigned int m_width;