From a1c317a8c4a460856ed2e882cb94c44264edc9ac Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:37:31 +0200 Subject: [PATCH] cellVdec: Postpone Context Destruction --- rpcs3/Emu/Cell/Modules/cellVdec.cpp | 45 +++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index 1847c8cfe5..16d42da682 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -298,9 +298,15 @@ struct vdec_context final } ~vdec_context() + { + release_decoder(); + } + + // Frees the FFmpeg decoder, which may take milliseconds + void release_decoder() { avcodec_free_context(&ctx); - sws_freeContext(sws); + sws_freeContext(std::exchange(sws, nullptr)); } static u32 freq_to_framerate_code(f64 freq) @@ -653,6 +659,38 @@ struct vdec_context final } }; +// Frees the FFmpeg decoders of closed contexts on a host thread. +// Freeing a decoder takes milliseconds, which would otherwise delay the guest thread closing it. +struct vdec_context_reaper +{ + lf_queue> contexts; + + void operator()() + { + while (thread_ctrl::state() != thread_state::aborting) + { + thread_ctrl::wait_on(contexts); + + // Let the closing sequence of the caller complete first: freeing this much memory slows down the other threads + thread_ctrl::wait_for(100'000); + + for (auto&& vdec : contexts.pop_all()) + { + if (vdec.use_count() > 1) + { + // Still used by an HLE call in progress, retry later + contexts.push(std::move(vdec)); + continue; + } + + vdec->release_decoder(); + } + } + } + + static constexpr auto thread_name = "HLE Video Decoder Reaper"sv; +}; + extern bool check_if_vdec_contexts_exist() { bool context_exists = false; @@ -1493,12 +1531,15 @@ error_code cellVdecClose(ppu_thread& ppu, u32 handle) vdec->seq_state = sequence_state::closed; vdec->mutex.lock_unlock(); - if (!idm::remove_verify(handle, std::move(vdec))) + if (!idm::remove_verify(handle, vdec)) { // Other thread removed it beforehead return { CELL_VDEC_ERROR_ARG, "remove_verify failed" }; } + // Let the reaper free the decoder so that the caller does not pay for it + g_fxo->get>().contexts.push(std::move(vdec)); + return CELL_OK; }