diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 15:55:00 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 20:49:48 -0400 |
| commit | 158e3733f25d61cfaeebbf312cafb389fb8e84f3 (patch) | |
| tree | e4c8f9b7e8914af06b883cfc482ef990b9346e39 | |
| parent | 0a43245033fc9407f30a239c856bcbf0d2dd619d (diff) | |
nx2: syncpt cancel always signals; a new slot request starts unsignalled
nn::syncpt::sdk's CancelSyncPointEvent sends 202 and then always waits on
the slot's event, so 202 now signals it also when no request was pending.
Title 1's movie player cancels each output-buffer slot before its first
request and hung there forever (the cup-opening screen stayed black and
the decoder was never started).
Slots are reused by different client objects: the movie player got slot
0 after NVN's fence waits had left its event signalled, read that as a
decoded frame, found none and aborted with 2111-3604. 201 now clears the
slot's event before registering the new request.
| -rw-r--r-- | src/core/hle/service/nx2_gsv.h | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h index e4c4939bce..9a90a27f79 100644 --- a/src/core/hle/service/nx2_gsv.h +++ b/src/core/hle/service/nx2_gsv.h @@ -213,6 +213,10 @@ private: } auto& request = m_requests[slot]; Cancel(request); + // A new request starts unsignalled. Slots are reused by different client objects: title 1's + // movie player got slot 0 after NVN's fence waits had left it signalled, took that as a + // decoded frame and aborted when the decoder had none. + event->Clear(system.Kernel()); request.syncpoint = syncpoint; request.pending = true; auto& manager = system.Host1x().GetSyncpointManager(); @@ -234,15 +238,20 @@ private: void CancelSlotEvent(HLERequestContext& ctx) { const u32 slot = ctx.CommandBuffer()[2]; LOG_INFO(Service, "nx2:syncpt TIPC 202 slot={:#x}", slot); - if (auto it = m_requests.find(slot); it != m_requests.end() && Cancel(it->second)) { - // The client waits on the slot's event after a cancel (NvOsEventPool, after its - // ~100 ms wait times out), so a cancel must signal it like a completion; it rechecks - // the counter (TIPC 103) itself. Title 1 hung forever there when the GPU thread ran - // a frame behind (EDEN_NX2_FRAMES_AHEAD) and a mid-frame wait outlasted 100 ms. - if (const auto event = m_events.find(slot); event != m_events.end()) { - event->second->Signal(system.Kernel()); - } + if (auto it = m_requests.find(slot); it != m_requests.end()) { + Cancel(it->second); + } + // nn::syncpt::sdk's cancel always waits on the slot's event after this reply, so a cancel + // signals it like a completion, also when nothing was pending; the client rechecks the + // counter (TIPC 103) itself. Title 1 hung forever there when the GPU thread ran a frame + // behind (EDEN_NX2_FRAMES_AHEAD) and a mid-frame wait outlasted NvOsEventPool's 100 ms, + // and its movie player cancels each slot before its first request: signalled only for + // pending requests, the player never got to start a movie. + auto*& event = m_events[slot]; + if (event == nullptr) { + event = m_context.CreateEvent(fmt::format("nx2:syncpt:Slot{:x}", slot)); } + event->Signal(system.Kernel()); TipcReplyWords(ctx, {}); } |
