aboutsummaryrefslogtreecommitdiff
path: root/src/core/core.cpp
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 01:55:20 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-30 02:31:58 -0400
commit0218eacfd560d0ce48b3ffe64d279ee85bbf1347 (patch)
treecbe764aa642b55cae06e94f15bd0e16f234e6052 /src/core/core.cpp
parent08be623a271d13b66497a98265f6832e9aa2c0d0 (diff)
nx2: run the guest counter at 31.25 MHz for Switch 2 titles
The Switch 2 counter runs at 31.25 MHz and its SDK converts ticks with that constant: Mario Kart World's sdk builds 31,250,000 in nn::os::GetSystemTickFrequency, TickManager::ConvertToTimeSpan and TickManager::ConvertToTick (19.2 MHz appears nowhere). Eglantine counted at the Switch 1 rate for every title, so every tick-based time in a Switch 2 game was off by 1.63x: elapsed times read short, and a deadline of one 60 Hz frame converted to ticks waited ~27 ms. The wall clock now has a guest counter frequency, set at boot: 31.25 MHz for Switch 2 programs, 19.2 MHz otherwise, EDEN_NX2_CNTFRQ=<Hz> to force one. CNTPCT reads, CNTFRQ_EL0, svcGetSystemTick, thread tick counts and the time services' tick conversions follow it, and the CNTPCT spin detection keeps its ~1.7 us threshold. Mario Kart World, title screen and attract demo, 180 s, same build: frame times unchanged (17-19 ms once warm), process CPU time 848 s instead of 930 s.
Diffstat (limited to 'src/core/core.cpp')
-rw-r--r--src/core/core.cpp25
1 files changed, 22 insertions, 3 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index f3d224b1ce..3728ccf8f7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -2,11 +2,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <array>
+#include <cstdlib>
#include <atomic>
#include <memory>
#include <utility>
#include "game_settings.h"
+#include "common/cpu_features.h"
#include "audio_core/audio_core.h"
#include "common/fs/fs.h"
#include "common/logging.h"
@@ -313,16 +315,33 @@ struct System::Impl {
fs_controller.InitTempStorage();
}
+ const bool switch2_program =
+ virtual_filesystem != nullptr &&
+ IsSwitch2Program(GetGameFileFromPath(virtual_filesystem, filepath));
+
// Switch 2 titles size their heap from the total memory and need the 12 GiB layout. Set it
// as a per-game value, so RestoreGlobalState brings back the configured layout afterwards.
- if (virtual_filesystem != nullptr &&
- Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_12Gb &&
- IsSwitch2Program(GetGameFileFromPath(virtual_filesystem, filepath))) {
+ if (switch2_program &&
+ Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_12Gb) {
LOG_INFO(Core, "Switch 2 program: using the 12 GiB memory layout");
Settings::values.memory_layout_mode.SetGlobal(false);
Settings::values.memory_layout_mode.SetValue(Settings::MemoryLayout::Memory_12Gb);
}
+ // The Switch 2 counter runs at 31.25 MHz and its SDK converts ticks with that constant
+ // (nn::os::GetSystemTickFrequency, TickManager::ConvertToTimeSpan/ConvertToTick). At 19.2 MHz
+ // every tick-based time in the game was off by 1.63x: elapsed times read short and a frame
+ // deadline in ticks waited ~27 ms. EDEN_NX2_CNTFRQ=<Hz> overrides it (A/B tests).
+ static const u64 forced_cntfrq = [] {
+ const char* value = std::getenv("EDEN_NX2_CNTFRQ");
+ return value != nullptr ? std::strtoull(value, nullptr, 10) : 0ULL;
+ }();
+ const u64 cntfrq = forced_cntfrq != 0 ? forced_cntfrq
+ : switch2_program ? Common::WallClock::Switch2CNTFRQ
+ : Common::WallClock::CNTFRQ;
+ Common::g_wall_clock.SetGuestCNTFRQ(cntfrq);
+ LOG_INFO(Core, "Guest counter frequency: {} Hz", cntfrq);
+
InitializeKernel(system);
if (params.applet_type == Service::AM::AppletType::Application) {