Accurately implement steady & system clocks (#732)
* Improve SteadyClock implementation accuracy * Rewrite system clocks to be accurate * Implement IStaticService 100 & 101 * Add time:* permissions * Address comments * Realign TimePermissions definitions * Address gdk's comments * Fix after rebase
This commit is contained in:
parent
4ad3936afd
commit
97d0c62423
13 changed files with 522 additions and 82 deletions
40
Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs
Normal file
40
Ryujinx.HLE/HOS/Services/Time/Clock/ClockTypes.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Ryujinx.HLE.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
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 * 1000000000 / frequency);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SteadyClockTimePoint
|
||||
{
|
||||
public ulong TimePoint;
|
||||
public UInt128 ClockSourceId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SystemClockContext
|
||||
{
|
||||
public ulong Offset;
|
||||
public SteadyClockTimePoint SteadyTimePoint;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
class StandardLocalSystemClockCore : SystemClockCore
|
||||
{
|
||||
private SteadyClockCore _steadyClockCore;
|
||||
private SystemClockContext _context;
|
||||
|
||||
private static StandardLocalSystemClockCore instance;
|
||||
|
||||
public static StandardLocalSystemClockCore Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new StandardLocalSystemClockCore(SteadyClockCore.Instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public StandardLocalSystemClockCore(SteadyClockCore steadyClockCore)
|
||||
{
|
||||
_steadyClockCore = steadyClockCore;
|
||||
_context = new SystemClockContext();
|
||||
|
||||
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
|
||||
}
|
||||
|
||||
public override ResultCode Flush(SystemClockContext context)
|
||||
{
|
||||
// TODO: set:sys SetUserSystemClockContext
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public override SteadyClockCore GetSteadyClockCore()
|
||||
{
|
||||
return _steadyClockCore;
|
||||
}
|
||||
|
||||
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
|
||||
{
|
||||
context = _context;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public override ResultCode SetSystemClockContext(SystemClockContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
class StandardNetworkSystemClockCore : SystemClockCore
|
||||
{
|
||||
private SteadyClockCore _steadyClockCore;
|
||||
private SystemClockContext _context;
|
||||
|
||||
private static StandardNetworkSystemClockCore instance;
|
||||
|
||||
public static StandardNetworkSystemClockCore Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new StandardNetworkSystemClockCore(SteadyClockCore.Instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public StandardNetworkSystemClockCore(SteadyClockCore steadyClockCore)
|
||||
{
|
||||
_steadyClockCore = steadyClockCore;
|
||||
_context = new SystemClockContext();
|
||||
|
||||
_context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
|
||||
}
|
||||
|
||||
public override ResultCode Flush(SystemClockContext context)
|
||||
{
|
||||
// TODO: set:sys SetNetworkSystemClockContext
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public override SteadyClockCore GetSteadyClockCore()
|
||||
{
|
||||
return _steadyClockCore;
|
||||
}
|
||||
|
||||
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
|
||||
{
|
||||
context = _context;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public override ResultCode SetSystemClockContext(SystemClockContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
class StandardUserSystemClockCore : SystemClockCore
|
||||
{
|
||||
private StandardLocalSystemClockCore _localSystemClockCore;
|
||||
private StandardNetworkSystemClockCore _networkSystemClockCore;
|
||||
private bool _autoCorrectionEnabled;
|
||||
|
||||
private static StandardUserSystemClockCore instance;
|
||||
|
||||
public static StandardUserSystemClockCore Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore)
|
||||
{
|
||||
_localSystemClockCore = localSystemClockCore;
|
||||
_networkSystemClockCore = networkSystemClockCore;
|
||||
_autoCorrectionEnabled = false;
|
||||
}
|
||||
|
||||
public override ResultCode Flush(SystemClockContext context)
|
||||
{
|
||||
return ResultCode.NotImplemented;
|
||||
}
|
||||
|
||||
public override SteadyClockCore GetSteadyClockCore()
|
||||
{
|
||||
return _localSystemClockCore.GetSteadyClockCore();
|
||||
}
|
||||
|
||||
public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
|
||||
{
|
||||
ResultCode result = ApplyAutomaticCorrection(thread, false);
|
||||
|
||||
context = new SystemClockContext();
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
return _localSystemClockCore.GetSystemClockContext(thread, out context);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override ResultCode SetSystemClockContext(SystemClockContext context)
|
||||
{
|
||||
return ResultCode.NotImplemented;
|
||||
}
|
||||
|
||||
private ResultCode ApplyAutomaticCorrection(KThread thread, bool autoCorrectionEnabled)
|
||||
{
|
||||
ResultCode result = ResultCode.Success;
|
||||
|
||||
if (_autoCorrectionEnabled != autoCorrectionEnabled && _networkSystemClockCore.IsClockSetup(thread))
|
||||
{
|
||||
result = _networkSystemClockCore.GetSystemClockContext(thread, out SystemClockContext context);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
_localSystemClockCore.SetSystemClockContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ResultCode SetAutomaticCorrectionEnabled(KThread thread, bool autoCorrectionEnabled)
|
||||
{
|
||||
ResultCode result = ApplyAutomaticCorrection(thread, autoCorrectionEnabled);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
_autoCorrectionEnabled = autoCorrectionEnabled;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool IsAutomaticCorrectionEnabled()
|
||||
{
|
||||
return _autoCorrectionEnabled;
|
||||
}
|
||||
}
|
||||
}
|
86
Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
Normal file
86
Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
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
|
||||
};
|
||||
|
||||
TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.ThreadState.CntpctEl0, thread.Context.ThreadState.CntfrqEl0);
|
||||
|
||||
result.TimePoint = _internalOffset.ToSeconds() + ticksTimeSpan.ToSeconds();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public UInt128 GetClockSourceId()
|
||||
{
|
||||
return _clockSourceId;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
Normal file
31
Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Time.Clock
|
||||
{
|
||||
abstract class SystemClockCore
|
||||
{
|
||||
public abstract SteadyClockCore GetSteadyClockCore();
|
||||
|
||||
public abstract ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context);
|
||||
|
||||
public abstract ResultCode SetSystemClockContext(SystemClockContext context);
|
||||
|
||||
public abstract ResultCode Flush(SystemClockContext context);
|
||||
|
||||
public bool IsClockSetup(KThread thread)
|
||||
{
|
||||
ResultCode result = GetSystemClockContext(thread, out SystemClockContext context);
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
SteadyClockCore steadyClockCore = GetSteadyClockCore();
|
||||
|
||||
SteadyClockTimePoint steadyClockTimePoint = steadyClockCore.GetCurrentTimePoint(thread);
|
||||
|
||||
return steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue