From d4dc5af3be1e5022b0bcb91184bf944e6be5ea08 Mon Sep 17 00:00:00 2001
From: Takoroka <137927095+Takoroka@users.noreply.github.com>
Date: Wed, 28 Oct 2026 04:56:14 -0400
Subject: [PATCH] nx2: frame-locked input replay and quieter logs
EDEN_NX2_KEY_RECORD records keyboard transitions as "f:code" against a
global count of guest presents; EDEN_NX2_KEY_SCRIPT replays them on the same
frames (or by seconds, as before), so a played session reproduces exactly.
Debug aids: the PC/LR/SP of the first unmapped reads, NSO load addresses,
EDEN_NX2_IPC_SUMMARY (request counts every 5 s), EDEN_NX2_FORCE_LOD0.
Unmapped-access logging is rate limited and the per-draw/semaphore spam is
demoted, so long sessions stay under the log size cap.
---
src/core/arm/dynarmic/arm_dynarmic_64.cpp | 20 +++++--
src/core/core.cpp | 2 +
src/core/core.h | 4 ++
src/core/hle/service/nx2_gsv.h | 1 +
src/core/hle/service/service.cpp | 57 +++++++++++++++++++
src/core/loader/nso.cpp | 1 +
src/core/memory.cpp | 14 ++++-
src/core/memory.h | 3 +
src/video_core/engines/puller.cpp | 4 +-
.../renderer_vulkan/vk_rasterizer.cpp | 2 +-
.../renderer_vulkan/vk_texture_cache.cpp | 5 ++
src/yuzu_cmd/emu_window/emu_window_sdl3.cpp | 23 ++++++++
src/yuzu_cmd/yuzu.cpp | 26 ++++++---
13 files changed, 145 insertions(+), 17 deletions(-)
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 894857a86d..c2bb05afba 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include
#include "common/settings.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
@@ -24,13 +25,24 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
+ u64 value{};
switch (size) {
- case sizeof(u64): return m_memory.Read64(vaddr);
- case sizeof(u32): return m_memory.Read32(vaddr);
- case sizeof(u16): return m_memory.Read16(vaddr);
- case sizeof(u8): return m_memory.Read8(vaddr);
+ case sizeof(u64): value = m_memory.Read64(vaddr); break;
+ case sizeof(u32): value = m_memory.Read32(vaddr); break;
+ case sizeof(u16): value = m_memory.Read16(vaddr); break;
+ case sizeof(u8): value = m_memory.Read8(vaddr); break;
default: UNREACHABLE();
}
+ // With memory aborts ignored the guest keeps running on zeros: say where it read from.
+ if (auto& unmapped = Core::Memory::UnmappedReadFlag(); unmapped != 0) {
+ unmapped = 0;
+ static std::atomic logged{};
+ if (logged.fetch_add(1) < 32) {
+ LOG_ERROR(Core_ARM, "Unmapped read {:#x} ({} bytes) at pc {:#x} lr {:#x} sp {:#x}", vaddr, size,
+ m_parent.m_jit->GetPC(), m_parent.m_jit->GetRegister(30), m_parent.m_jit->GetSP());
+ }
+ }
+ return value;
}
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 06543b0833..eee91ef4fc 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -68,6 +68,8 @@
namespace Core {
+std::atomic g_nx2_present_count{};
+
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
diff --git a/src/core/core.h b/src/core/core.h
index d88ed64151..0276679d76 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -6,6 +6,7 @@
#pragma once
+#include
#include
#include
#include
@@ -115,6 +116,9 @@ class RenderdocAPI;
namespace Core {
+/// Frames the guest has presented (Switch 2 vi2 present calls); input replay is locked to it.
+extern std::atomic g_nx2_present_count;
+
class CpuManager;
class Debugger;
class DeviceMemory;
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 89f3a9fd50..302c5a41a8 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -564,6 +564,7 @@ private:
void PresentClientBuffer(HLERequestContext& ctx) {
const auto* w = ctx.CommandBuffer();
++m_presents;
+ Core::g_nx2_present_count.fetch_add(1, std::memory_order_relaxed);
if (m_presents <= 4 || (m_presents % 600) == 0) {
LOG_INFO(Service, "nx2:vi2 229 present #{} token={:#x} slot={} image={}", m_presents,
u64{w[2]} | (u64{w[3]} << 32), w[4], w[5]);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 931de1c3ef..df929e8193 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -4,6 +4,13 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/scope_exit.h"
+#include
+#include
+#include
+#include