From 470a8031a44145f906b50919ce1a4e01d51c15ff Mon Sep 17 00:00:00 2001 From: Mary Date: Mon, 8 May 2023 00:15:58 +0200 Subject: [PATCH] time: Update for 15.0.0 changes and fixes long standing issues (#4822) * time: Update for 15.0.0 changes Last time we did an upgrade on the time service was during 9.x era, it was about time to take back that reverse again! 15.0.0 added a new structure on the shared memory to get steady clock raw timepoints with a granularity in nanoseconds. This commit implements this new part. I plan to write a follow up with a bit of refactoring of this ancient part of the emulator. As always, reverse and work done by your truly. PS: As a reminder, if this change is reused anywhere else, work should be credited as Ryujinx and not my person. * time: Do not set setup value to posix time This should fix local and network clock returning 0 under usage with shared memory. This probably fix #2430. * Address gdkchan's comment * Fix internal offset not working since changes and ensure that user clock have a valid clock id * time: Report auto correcting clock and hardcode steady clock unique id Fix Pokemon Sword Pokejobs for real. * Address gdkchan's comment --- src/Ryujinx.HLE/HOS/Horizon.cs | 11 ++-- .../Types/ContinuousAdjustmentTimePoint.cs | 13 +++++ .../HOS/Services/Time/TimeSharedMemory.cs | 53 +++++++++++++------ 3 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 1639532ed8..f73dea1d90 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -200,9 +200,10 @@ namespace Ryujinx.HLE.HOS LibHacHorizonManager = device.Configuration.LibHacHorizonManager; + // We hardcode a clock source id to avoid it changing between each start. // TODO: use set:sys (and get external clock source id from settings) // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate. - UInt128 clockSourceId = UInt128Utils.CreateRandom(); + UInt128 clockSourceId = new UInt128(0x36a0328702ce8bc1, 0x1608eaba02333284); IRtcManager.GetExternalRtcValue(out ulong rtcValue); // We assume the rtc is system time. @@ -222,22 +223,22 @@ namespace Ryujinx.HLE.HOS internalOffset = internalOffset.AddSeconds(-3600L); } - internalOffset = new TimeSpanType(-internalOffset.NanoSeconds); + systemTime = new TimeSpanType(systemTime.NanoSeconds + internalOffset.NanoSeconds); // First init the standard steady clock - TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, systemTime, internalOffset, TimeSpanType.Zero, false); + TimeServiceManager.Instance.SetupStandardSteadyClock(TickSource, clockSourceId, TimeSpanType.Zero, TimeSpanType.Zero, TimeSpanType.Zero, false); TimeServiceManager.Instance.SetupStandardLocalSystemClock(TickSource, new SystemClockContext(), systemTime.ToSeconds()); + TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext); if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes)) { TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000); // The network system clock needs a valid system clock, as such we setup this system clock using the local system clock. - TimeServiceManager.Instance.StandardLocalSystemClock.GetClockContext(TickSource, out SystemClockContext localSytemClockContext); TimeServiceManager.Instance.SetupStandardNetworkSystemClock(localSytemClockContext, standardNetworkClockSufficientAccuracy); } - TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, false, SteadyClockTimePoint.GetRandom()); + TimeServiceManager.Instance.SetupStandardUserSystemClock(TickSource, true, localSytemClockContext.SteadyTimePoint); // FIXME: TimeZone should be init here but it's actually done in ContentManager diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs new file mode 100644 index 0000000000..b57dfaa061 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/ContinuousAdjustmentTimePoint.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Time.Clock.Types +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ContinuousAdjustmentTimePoint + { + public ulong ClockOffset; + public long Multiplier; + public long DivisorLog2; + public SystemClockContext Context; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs index 7063290bc0..6b1e16875b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs @@ -1,8 +1,8 @@ using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Services.Time.Clock; +using Ryujinx.HLE.HOS.Services.Time.Clock.Types; using Ryujinx.HLE.HOS.Services.Time.Types; -using Ryujinx.HLE.Utilities; using System; using System.Runtime.CompilerServices; using System.Threading; @@ -16,10 +16,11 @@ namespace Ryujinx.HLE.HOS.Services.Time private SharedMemoryStorage _timeSharedMemoryStorage; private int _timeSharedMemorySize; - private const uint SteadyClockContextOffset = 0x00; - private const uint LocalSystemClockContextOffset = 0x38; - private const uint NetworkSystemClockContextOffset = 0x80; + private const uint SteadyClockContextOffset = 0x00; + private const uint LocalSystemClockContextOffset = 0x38; + private const uint NetworkSystemClockContextOffset = 0x80; private const uint AutomaticCorrectionEnabledOffset = 0xC8; + private const uint ContinuousAdjustmentTimePointOffset = 0xD0; public void Initialize(Switch device, KSharedMemory sharedMemory, SharedMemoryStorage timeSharedMemoryStorage, int timeSharedMemorySize) { @@ -39,15 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.Time public void SetupStandardSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint) { - TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); - - SteadyClockContext context = new SteadyClockContext - { - InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds), - ClockSourceId = clockSourceId - }; - - WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context); + UpdateSteadyClock(tickSource, clockSourceId, currentTimePoint); } public void SetAutomaticCorrectionEnabled(bool isAutomaticCorrectionEnabled) @@ -58,10 +51,38 @@ namespace Ryujinx.HLE.HOS.Services.Time public void SetSteadyClockRawTimePoint(ITickSource tickSource, TimeSpanType currentTimePoint) { - SteadyClockContext context = ReadObjectFromSharedMemory(SteadyClockContextOffset, 4); - TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); + SteadyClockContext context = ReadObjectFromSharedMemory(SteadyClockContextOffset, 4); - context.InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds); + UpdateSteadyClock(tickSource, context.ClockSourceId, currentTimePoint); + } + + private void UpdateSteadyClock(ITickSource tickSource, UInt128 clockSourceId, TimeSpanType currentTimePoint) + { + TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(tickSource.Counter, tickSource.Frequency); + + ContinuousAdjustmentTimePoint adjustmentTimePoint = new ContinuousAdjustmentTimePoint + { + ClockOffset = (ulong)ticksTimeSpan.NanoSeconds, + Multiplier = 1, + DivisorLog2 = 0, + Context = new SystemClockContext + { + Offset = 0, + SteadyTimePoint = new SteadyClockTimePoint + { + ClockSourceId = clockSourceId, + TimePoint = 0 + } + } + }; + + WriteObjectToSharedMemory(ContinuousAdjustmentTimePointOffset, 4, adjustmentTimePoint); + + SteadyClockContext context = new SteadyClockContext + { + InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds), + ClockSourceId = clockSourceId + }; WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context); }