Improve SteadyClock implementation accuracy

This commit is contained in:
Thog 2019-07-13 23:38:27 +02:00
parent 4ad3936afd
commit 5a7b8122a4
No known key found for this signature in database
GPG key ID: 0CD291558FAFDBC6
4 changed files with 146 additions and 14 deletions

View file

@ -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)

View 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;
}
}

View 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;
}
}
}

View file

@ -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;
}