From 701e0e97fe8ea5a0d85113de8cad3ced738ea76f Mon Sep 17 00:00:00 2001 From: loki Date: Thu, 16 Jan 2020 21:32:53 +0100 Subject: [PATCH] Fix loss of connection when switching display resolution on windows --- sunshine/platform/common.h | 1 - sunshine/platform/linux.cpp | 14 --- sunshine/platform/windows_dxgi.cpp | 139 ++++++++++++++--------------- sunshine/video.cpp | 17 +++- 4 files changed, 81 insertions(+), 90 deletions(-) diff --git a/sunshine/platform/common.h b/sunshine/platform/common.h index ce477a71..3aaf8e50 100644 --- a/sunshine/platform/common.h +++ b/sunshine/platform/common.h @@ -33,7 +33,6 @@ enum class capture_e : int { class display_t { public: virtual capture_e snapshot(img_t *img, bool cursor) = 0; - virtual int reinit() = 0; virtual std::unique_ptr alloc_img() = 0; virtual ~display_t() = default; diff --git a/sunshine/platform/linux.cpp b/sunshine/platform/linux.cpp index afafa1a3..40615d61 100644 --- a/sunshine/platform/linux.cpp +++ b/sunshine/platform/linux.cpp @@ -164,12 +164,6 @@ struct x11_attr_t : public display_t { return capture_e::ok; } - int reinit() override { - refresh(); - - return 0; - } - std::unique_ptr alloc_img() override { return std::make_unique(); } @@ -247,14 +241,6 @@ struct shm_attr_t : public x11_attr_t { return std::make_unique(); } - int reinit() override { - data.~shm_data_t(); - shm_id.~shm_id_t(); - xcb.reset(nullptr); - - return init(); - } - int init() { shm_xdisplay.reset(XOpenDisplay(nullptr)); xcb.reset(xcb_connect(nullptr, nullptr)); diff --git a/sunshine/platform/windows_dxgi.cpp b/sunshine/platform/windows_dxgi.cpp index e355bc8b..fef95e1e 100644 --- a/sunshine/platform/windows_dxgi.cpp +++ b/sunshine/platform/windows_dxgi.cpp @@ -53,6 +53,7 @@ public: case DXGI_ERROR_WAIT_TIMEOUT: return capture_e::timeout; case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: case DXGI_ERROR_ACCESS_DENIED: return capture_e::reinit; default: @@ -82,7 +83,9 @@ public: case DXGI_ERROR_WAIT_TIMEOUT: return capture_e::timeout; case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: case DXGI_ERROR_ACCESS_DENIED: + has_frame = false; return capture_e::reinit; default: BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view(); @@ -320,78 +323,6 @@ public: return capture_e::ok; } - /* - * Called when access is lost. Dup must be reinitialized - */ - int reinit() override { - HRESULT status; - - dup.reset(); - //TODO: Use IDXGIOutput5 for improved performance - { - dxgi::output1_t::pointer output1_p {}; - status = output->QueryInterface(IID_IDXGIOutput1, (void**)&output1_p); - dxgi::output1_t output1 {output1_p }; - - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; - return -1; - } - - // We try this twice, in case we still get an error on reinitialization - for(int x = 0; x < 2; ++x) { - dxgi::dup_t::pointer dup_p {}; - status = output1->DuplicateOutput((IUnknown*)device.get(), &dup_p); - if(SUCCEEDED(status)) { - dup.reset(dup_p); - break; - } - std::this_thread::sleep_for(200ms); - } - - if(FAILED(status)) { - BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - } - - DXGI_OUTDUPL_DESC dup_desc; - dup.dup->GetDesc(&dup_desc); - - format = dup_desc.ModeDesc.Format; - - BOOST_LOG(debug) << "Source format ["sv << format_str[dup_desc.ModeDesc.Format] << ']'; - - D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; - t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_STAGING; - t.Format = format; - t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - dxgi::texture2d_t::pointer tex_p {}; - status = device->CreateTexture2D(&t, nullptr, &tex_p); - - texture.reset(tex_p); - - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - // map the texture simply to get the pitch and stride - status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img); - if(FAILED(status)) { - BOOST_LOG(error) << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - return 0; - } - std::unique_ptr<::platf::img_t> alloc_img() override { auto img = std::make_unique(); @@ -545,7 +476,69 @@ public: dxgi->SetMaximumFrameLatency(1); } - return reinit(); + //TODO: Use IDXGIOutput5 for improved performance + { + dxgi::output1_t::pointer output1_p {}; + status = output->QueryInterface(IID_IDXGIOutput1, (void**)&output1_p); + dxgi::output1_t output1 {output1_p }; + + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; + return -1; + } + + // We try this twice, in case we still get an error on reinitialization + for(int x = 0; x < 2; ++x) { + dxgi::dup_t::pointer dup_p {}; + status = output1->DuplicateOutput((IUnknown*)device.get(), &dup_p); + if(SUCCEEDED(status)) { + dup.reset(dup_p); + break; + } + std::this_thread::sleep_for(200ms); + } + + if(FAILED(status)) { + BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + } + + DXGI_OUTDUPL_DESC dup_desc; + dup.dup->GetDesc(&dup_desc); + + format = dup_desc.ModeDesc.Format; + + BOOST_LOG(debug) << "Source format ["sv << format_str[dup_desc.ModeDesc.Format] << ']'; + + D3D11_TEXTURE2D_DESC t {}; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_STAGING; + t.Format = format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + dxgi::texture2d_t::pointer tex_p {}; + status = device->CreateTexture2D(&t, nullptr, &tex_p); + + texture.reset(tex_p); + + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + // map the texture simply to get the pitch and stride + status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, ¤t_img); + if(FAILED(status)) { + BOOST_LOG(error) << "Error: Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + return 0; } ~display_t() override { diff --git a/sunshine/video.cpp b/sunshine/video.cpp index 4839bc1a..7a10b04d 100644 --- a/sunshine/video.cpp +++ b/sunshine/video.cpp @@ -203,11 +203,24 @@ void capture_display(packet_queue_t packets, idr_event_t idr_events, config_t co auto status = disp->snapshot(img.get(), display_cursor); switch(status) { - case platf::capture_e::reinit: - if(disp->reinit()) { + case platf::capture_e::reinit: { + // We try this twice, in case we still get an error on reinitialization + for(int x = 0; x < 2; ++x) { + disp.reset(); + disp = platf::display(); + + if (disp) { + break; + } + + std::this_thread::sleep_for(200ms); + } + + if (!disp) { packets->stop(); } continue; + } case platf::capture_e::timeout: std::this_thread::sleep_until(next_snapshot); continue;