aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYuri Olive <yuriso1994@gmail.com>2026-10-31 15:14:38 -0300
committerJonas <baguettegamerofficial2020@gmail.com>2026-11-03 05:55:03 +0600
commitbfd6430918ff4eda8c8c0e5fd0a927b3f64ff258 (patch)
treef3e12d0e4ace81fd29911dbc2bc4e7bb190091a2 /src
parent8d6cb5c0d4fbab0e88baefa2825849503776c364 (diff)
service: nx2:vdecsvc: handle end-of-stream without aborting
- Complete pending fences immediately on release once the stream ends and no frames remain to be processed, preventing infinite waits in movie teardown. - Stop re-arming m_ready->room when an end-of-stream packet arrives or the final frame is output, avoiding spurious wakeups and subsequent empty queue queries. - Reset m_stream_ended when new input is submitted or on Flush/Stop. Fixes an abort (2111-3604 / 0x1c286f) in Donkey Kong Bananza during the tutorial transition scene when the video decoder stream ends. Assisted-by: Gemini (vdecsvc end-of-stream fence completion and timer de-arming) (cherry picked from commit b180df1ed77568da7c3e78dd97c0c46d84e54494)
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/nx2_gsv.h27
1 files changed, 24 insertions, 3 deletions
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 259681a2a2..e488be098c 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -2513,7 +2513,19 @@ private:
if (release_fence) {
m_release_fences[image] = *release_fence;
}
- value = m_sync_base + ++m_handed;
+ // After the end of a stream nothing fills an image again, so a release completes at
+ // once: its fence is the syncpoint's current value. Mario Kart World releases its
+ // images after a movie ends and waits for the returned fences; a fence for a frame
+ // that never comes hung the game on a black screen after its title movie.
+ if (release_fence && m_stream_ended && m_inputs.empty() && m_decoded.empty()) {
+ const u32 reached = static_cast<u32>(
+ system.Host1x().GetSyncpointManager().GetGuestSyncpointValue(m_syncpoint));
+ m_sync_base = reached;
+ m_handed = 0;
+ value = reached;
+ } else {
+ value = m_sync_base + ++m_handed;
+ }
}
m_cv.notify_one();
return {~m_syncpoint, 0, value, 0};
@@ -2559,7 +2571,8 @@ private:
{
std::scoped_lock lk{m_mutex};
m_inputs.push_back(std::move(input));
- m_ready->room.store(m_inputs.size() < MaxQueuedInputs, std::memory_order_relaxed);
+ m_stream_ended = false;
+ m_ready->room.store(!end && m_inputs.size() < MaxQueuedInputs, std::memory_order_relaxed);
}
m_cv.notify_one();
const u64 id = ++m_submitted;
@@ -2574,6 +2587,7 @@ private:
std::scoped_lock lk{m_mutex};
m_inputs.clear();
m_flush = true;
+ m_stream_ended = false;
m_ready->room.store(true, std::memory_order_relaxed);
}
m_cv.notify_one();
@@ -2647,6 +2661,10 @@ private:
lk.lock();
// Even an image written during a flush counts: the client waits for its value.
m_outputs.push_back({image, width, height, frame.pts, frame.end});
+ if (frame.end) {
+ m_stream_ended = true;
+ m_ready->room.store(false, std::memory_order_relaxed);
+ }
system.Host1x().GetSyncpointManager().IncrementGuest(m_syncpoint);
++m_frames_out;
if (m_frames_out == 1 || (m_frames_out % 600) == 0 || frame.end) {
@@ -2658,7 +2676,9 @@ private:
}
Input input = std::move(m_inputs.front());
m_inputs.pop_front();
- m_ready->room.store(m_inputs.size() < MaxQueuedInputs, std::memory_order_relaxed);
+ if (!m_stream_ended) {
+ m_ready->room.store(m_inputs.size() < MaxQueuedInputs, std::memory_order_relaxed);
+ }
const std::string codec = m_codec;
lk.unlock();
if (!decoder || decoder->Name() != codec) {
@@ -2809,6 +2829,7 @@ private:
std::deque<u32> m_free; ///< Output images, in the order the client acquires them
std::map<u32, Fence> m_release_fences;
std::deque<Output> m_outputs; ///< Finished, for 213
+ bool m_stream_ended{}; ///< End-of-stream frame handed out, nothing submitted since
bool m_flush{};
bool m_quit{};
std::thread m_worker;