Improve SteadyClock implementation accuracy
This commit is contained in:
parent
4ad3936afd
commit
5a7b8122a4
4 changed files with 146 additions and 14 deletions
|
@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
|
|||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Sm;
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Loaders.Npdm;
|
||||
|
@ -195,6 +196,11 @@ namespace Ryujinx.HLE.HOS
|
|||
LoadKeySet();
|
||||
|
||||
ContentManager = new ContentManager(device);
|
||||
|
||||
// NOTE: Now we set the default internal offset of the steady clock like Nintendo does... even if it's strange this is accurate.
|
||||
// TODO: use bpc:r and set:sys (and set external clock source id from settings)
|
||||
DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
SteadyClockCore.Instance.SetInternalOffset(new TimeSpanType(((ulong)(DateTime.Now.ToUniversalTime() - UnixEpoch).TotalSeconds) * 1000));
|
||||
}
|
||||
|
||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||
|
|
34
Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs
Normal file
34
Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TimeSpanType
|
||||
{
|
||||
public ulong NanoSeconds;
|
||||
|
||||
public TimeSpanType(ulong nanoSeconds)
|
||||
{
|
||||
NanoSeconds = nanoSeconds;
|
||||
}
|
||||
|
||||
public ulong ToSeconds()
|
||||
{
|
||||
return NanoSeconds * 1000000000;
|
||||
}
|
||||
|
||||
public static TimeSpanType FromTicks(ulong ticks, ulong frequency)
|
||||
{
|
||||
return new TimeSpanType((ticks * 1000) / frequency);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SteadyClockTimePoint
|
||||
{
|
||||
public ulong TimePoint;
|
||||
public UInt128 ClockSourceId;
|
||||
}
|
||||
}
|
78
Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
Normal file
78
Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
class SteadyClockCore
|
||||
{
|
||||
private TimeSpanType _testOffset;
|
||||
private TimeSpanType _internalOffset;
|
||||
private UInt128 _clockSourceId;
|
||||
|
||||
private static SteadyClockCore instance;
|
||||
|
||||
public static SteadyClockCore Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new SteadyClockCore();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
private SteadyClockCore()
|
||||
{
|
||||
_testOffset = new TimeSpanType(0);
|
||||
_internalOffset = new TimeSpanType(0);
|
||||
_clockSourceId = new UInt128(Guid.NewGuid().ToByteArray());
|
||||
}
|
||||
|
||||
private SteadyClockTimePoint GetTimePoint(KThread thread)
|
||||
{
|
||||
SteadyClockTimePoint result = new SteadyClockTimePoint
|
||||
{
|
||||
TimePoint = 0,
|
||||
ClockSourceId = _clockSourceId
|
||||
};
|
||||
|
||||
result.TimePoint = _internalOffset.ToSeconds() + TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0).ToSeconds();
|
||||
return result;
|
||||
}
|
||||
|
||||
public SteadyClockTimePoint GetCurrentTimePoint(KThread thread)
|
||||
{
|
||||
SteadyClockTimePoint result = GetTimePoint(thread);
|
||||
|
||||
result.TimePoint += _testOffset.ToSeconds();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public TimeSpanType GetTestOffset()
|
||||
{
|
||||
return _testOffset;
|
||||
}
|
||||
|
||||
public void SetTestOffset(TimeSpanType testOffset)
|
||||
{
|
||||
_testOffset = testOffset;
|
||||
}
|
||||
|
||||
// TODO: check if this is accurate
|
||||
public TimeSpanType GetInternalOffset()
|
||||
{
|
||||
return _internalOffset;
|
||||
}
|
||||
|
||||
// TODO: check if this is accurate
|
||||
public void SetInternalOffset(TimeSpanType internalOffset)
|
||||
{
|
||||
_internalOffset = internalOffset;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +1,18 @@
|
|||
using System;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time
|
||||
{
|
||||
class ISteadyClock : IpcService
|
||||
{
|
||||
private ulong _testOffset;
|
||||
|
||||
public ISteadyClock()
|
||||
{
|
||||
_testOffset = 0;
|
||||
}
|
||||
|
||||
[Command(0)]
|
||||
// GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint
|
||||
public ResultCode GetCurrentTimePoint(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((long)(System.Diagnostics.Process.GetCurrentProcess().StartTime - DateTime.Now).TotalSeconds);
|
||||
SteadyClockTimePoint currentTimePoint = SteadyClockCore.Instance.GetCurrentTimePoint(context.Thread);
|
||||
|
||||
for (int i = 0; i < 0x10; i++)
|
||||
{
|
||||
context.ResponseData.Write((byte)0);
|
||||
}
|
||||
context.ResponseData.WriteStruct(currentTimePoint);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -29,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
// GetTestOffset() -> nn::TimeSpanType
|
||||
public ResultCode GetTestOffset(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write(_testOffset);
|
||||
context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetTestOffset());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -38,7 +30,29 @@ namespace Ryujinx.HLE.HOS.Services.Time
|
|||
// SetTestOffset(nn::TimeSpanType)
|
||||
public ResultCode SetTestOffset(ServiceCtx context)
|
||||
{
|
||||
_testOffset = context.RequestData.ReadUInt64();
|
||||
TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>();
|
||||
|
||||
SteadyClockCore.Instance.SetTestOffset(testOffset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[Command(200)] // 3.0.0+
|
||||
// GetInternalOffset() -> nn::TimeSpanType
|
||||
public long GetInternalOffset(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.WriteStruct(SteadyClockCore.Instance.GetInternalOffset());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[Command(201)] // 3.0.0-3.0.2
|
||||
// SetInternalOffset(nn::TimeSpanType)
|
||||
public long SetInternalOffset(ServiceCtx context)
|
||||
{
|
||||
TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>();
|
||||
|
||||
SteadyClockCore.Instance.SetInternalOffset(internalOffset);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue