diff --git a/src/nvenc/common_impl/nvenc_base.cpp b/src/nvenc/common_impl/nvenc_base.cpp index c3ce0900772..c58ff703868 100644 --- a/src/nvenc/common_impl/nvenc_base.cpp +++ b/src/nvenc/common_impl/nvenc_base.cpp @@ -542,7 +542,7 @@ namespace nvenc { NV_ENC_LOCK_BITSTREAM lock_bitstream = { NV_ENC_LOCK_BITSTREAM_VER }; lock_bitstream.outputBitstream = output_bitstream; - lock_bitstream.doNotWait = 0; + lock_bitstream.doNotWait = async_event_handle ? 1 : 0; if (async_event_handle && !wait_for_async_event(100)) { BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout"; diff --git a/src/nvenc/win/impl/nvenc_d3d11_base.cpp b/src/nvenc/win/impl/nvenc_d3d11_base.cpp index d8f872e2dc3..53376adc646 100644 --- a/src/nvenc/win/impl/nvenc_d3d11_base.cpp +++ b/src/nvenc/win/impl/nvenc_d3d11_base.cpp @@ -9,9 +9,19 @@ namespace NVENC_NAMESPACE { #else namespace nvenc { #endif + nvenc_d3d11_base::nvenc_d3d11_base(NV_ENC_DEVICE_TYPE device_type, shared_dll dll): + nvenc_base(device_type), + dll(dll) { + async_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + } - bool - nvenc_d3d11_base::init_library() { + nvenc_d3d11_base::~nvenc_d3d11_base() { + if (async_event_handle) { + CloseHandle(async_event_handle); + } + } + + bool nvenc_d3d11_base::init_library() { if (nvenc) return true; if (!dll) return false; @@ -32,4 +42,8 @@ namespace nvenc { return false; } + + bool nvenc_d3d11_base::wait_for_async_event(uint32_t timeout_ms) { + return WaitForSingleObject(async_event_handle, timeout_ms) == WAIT_OBJECT_0; + } } diff --git a/src/nvenc/win/impl/nvenc_d3d11_base.h b/src/nvenc/win/impl/nvenc_d3d11_base.h index 9c93e382dbf..b9871d16450 100644 --- a/src/nvenc/win/impl/nvenc_d3d11_base.h +++ b/src/nvenc/win/impl/nvenc_d3d11_base.h @@ -28,13 +28,11 @@ namespace nvenc { */ class nvenc_d3d11_base: public nvenc_base, virtual public nvenc_d3d11 { public: - nvenc_d3d11_base(NV_ENC_DEVICE_TYPE device_type, shared_dll dll): - nvenc_base(device_type), - dll(dll) {} - + explicit nvenc_d3d11_base(NV_ENC_DEVICE_TYPE device_type, shared_dll dll); + ~nvenc_d3d11_base(); protected: - bool - init_library() override; + bool init_library() override; + bool wait_for_async_event(uint32_t timeout_ms) override; private: shared_dll dll; diff --git a/src/video.cpp b/src/video.cpp index 4df5b76ff1c..6c01bc4389e 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -281,6 +281,7 @@ namespace video { REF_FRAMES_INVALIDATION = 1 << 8, ///< Support reference frames invalidation ALWAYS_REPROBE = 1 << 9, ///< This is an encoder of last resort and we want to aggressively probe for a better one YUV444_SUPPORT = 1 << 10, ///< Encoder may support 4:4:4 chroma sampling depending on hardware + ASYNC_TEARDOWN = 1 << 11, ///< Encoder supports async teardown on a different thread }; class avcodec_encode_session_t: public encode_session_t { @@ -483,7 +484,7 @@ namespace video { {}, // Fallback options "h264_nvenc"s, }, - PARALLEL_ENCODING | REF_FRAMES_INVALIDATION | YUV444_SUPPORT // flags + PARALLEL_ENCODING | REF_FRAMES_INVALIDATION | YUV444_SUPPORT | ASYNC_TEARDOWN // flags }; #elif !defined(__APPLE__) encoder_t nvenc { @@ -1796,6 +1797,23 @@ namespace video { return; } + // As a workaround for NVENC hangs and to generally speed up encoder reinit, + // we will complete the encoder teardown in a separate thread if supported. + // This will move expensive processing off the encoder thread to allow us + // to restart encoding as soon as possible. For cases where the NVENC driver + // hang occurs, this thread may probably never exit, but it will allow + // streaming to continue without requiring a full restart of Sunshine. + auto fail_guard = util::fail_guard([&encoder, &session] { + if (encoder.flags & ASYNC_TEARDOWN) { + std::thread encoder_teardown_thread {[session = std::move(session)]() mutable { + BOOST_LOG(info) << "Starting async encoder teardown"; + session.reset(); + BOOST_LOG(info) << "Async encoder teardown complete"; + }}; + encoder_teardown_thread.detach(); + } + }); + // set minimum frame time, avoiding violation of client-requested target framerate auto minimum_frame_time = std::chrono::milliseconds(1000 / std::min(config.framerate, (config::video.min_fps_factor * 10))); BOOST_LOG(debug) << "Minimum frame time set to "sv << minimum_frame_time.count() << "ms, based on min fps factor of "sv << config::video.min_fps_factor << "."sv;