diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-28 12:31:44 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-28 12:31:44 -0400 |
| commit | b4268e6e67e6f4e11e0a5548b364f108fbaeabb1 (patch) | |
| tree | 9f90321029f69ab23a849565a00563a2d6331cb7 /src | |
| parent | a33a21563f08b05c584a61bc5134638615349db1 (diff) | |
Switch 2: signal nx2:vi2 31/32 events on every vblank
Title 1's main thread waits on the vi2 32 event until it has fired twice
before it submits GPU work; Eden's stub never signalled it and the title
hung before its first frame. Signal the 31 and 32 events at 60 Hz.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/nx2_gsv.h | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h index 6acea5602a..c25359bd58 100644 --- a/src/core/hle/service/nx2_gsv.h +++ b/src/core/hle/service/nx2_gsv.h @@ -15,6 +15,7 @@ #include <map> #include <optional> #include <memory> +#include <mutex> #include <vector> #include "common/alignment.h" @@ -593,14 +594,17 @@ public: {3, &INx2Vi2Session::ReturnOne, "GetDisplayCount (guessed)"}, {5, &INx2Vi2Session::GetTimestampRecorderCount, "GetTimestampRecorderCount (guessed)"}, {11, &INx2Vi2Session::ReturnEvent, "Unknown11"}, - {31, &INx2Vi2Session::ReturnEvent, "Unknown31"}, - {32, &INx2Vi2Session::ReturnEvent, "Unknown32"}, + {31, &INx2Vi2Session::GetVsyncEvent, "GetVsyncEvent (guessed)"}, + {32, &INx2Vi2Session::GetVsyncEvent, "GetDisplayVsyncEvent (guessed)"}, {228, &INx2Vi2Session::CancelClientBuffer, "CancelClientBuffer"}, {229, &INx2Vi2Session::PresentClientBuffer, "PresentClientBuffer"}, }); } ~INx2Vi2Session() override { + if (m_vsync_timer) { + system.CoreTiming().UnscheduleEvent(m_vsync_timer); + } for (auto* event : m_events) { m_context.CloseEvent(event); } @@ -801,9 +805,41 @@ private: rb.PushCopyObjects(ctx, event->GetReadableEvent()); } + // 31(u32 kind) and 32(u32 display): events signalled on every vblank. Title 1's main thread + // waits on the 32 event until it has fired twice before it submits any GPU work, and hung + // there while it never fired. + void GetVsyncEvent(HLERequestContext& ctx) { + LOG_INFO(Service, "nx2:vi2 {} vsync event, [2]={:#x}", ctx.GetCommand(), ctx.CommandBuffer()[2]); + auto* event = m_context.CreateEvent(fmt::format("nx2:vi2:Vsync{}", m_events.size())); + { + std::scoped_lock lk{m_vsync_mutex}; + m_events.push_back(event); + m_vsync_events.push_back(event); + } + if (!m_vsync_timer) { + m_vsync_timer = Core::Timing::CreateEvent( + "nx2:vi2 vsync", + [this](s64, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { + std::scoped_lock lk{m_vsync_mutex}; + for (auto* vsync : m_vsync_events) { + vsync->Signal(system.Kernel()); + } + return std::nullopt; + }); + constexpr std::chrono::nanoseconds VblankInterval{16'666'667}; + system.CoreTiming().ScheduleLoopingEvent(VblankInterval, VblankInterval, m_vsync_timer); + } + IPC::ResponseBuilder rb{ctx, 2, 1, 0}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(ctx, event->GetReadableEvent()); + } + KernelHelpers::ServiceContext m_context; std::shared_ptr<Nx2NativeObjects> m_objects; std::vector<Kernel::KEvent*> m_events; + std::mutex m_vsync_mutex; + std::vector<Kernel::KEvent*> m_vsync_events; + std::shared_ptr<Core::Timing::EventType> m_vsync_timer; u64 m_presents{}; std::atomic<bool> m_capture_pending{}; }; |
