diff --git a/ARMeilleure/Translation/ArmEmitterContext.cs b/ARMeilleure/Translation/ArmEmitterContext.cs index 33355daec9..48254de4e7 100644 --- a/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/ARMeilleure/Translation/ArmEmitterContext.cs @@ -6,7 +6,6 @@ using ARMeilleure.Instructions; using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Memory; using ARMeilleure.State; -using ARMeilleure.Translation.PTC; using System; using System.Collections.Generic; using System.Reflection; @@ -44,14 +43,13 @@ namespace ARMeilleure.Translation public IMemoryManager Memory { get; } - public bool HasPtc { get; } - public EntryTable CountTable { get; } public AddressTable FunctionTable { get; } public TranslatorStubs Stubs { get; } public ulong EntryAddress { get; } public bool HighCq { get; } + public bool HasPtc { get; } public Aarch32Mode Mode { get; } private int _ifThenBlockStateIndex = 0; @@ -66,15 +64,16 @@ namespace ARMeilleure.Translation TranslatorStubs stubs, ulong entryAddress, bool highCq, + bool hasPtc, Aarch32Mode mode) { - HasPtc = Ptc.State != PtcState.Disabled; Memory = memory; CountTable = countTable; FunctionTable = funcTable; Stubs = stubs; EntryAddress = entryAddress; HighCq = highCq; + HasPtc = hasPtc; Mode = mode; _labels = new Dictionary(); diff --git a/ARMeilleure/Translation/PTC/IPtcLoadState.cs b/ARMeilleure/Translation/PTC/IPtcLoadState.cs new file mode 100644 index 0000000000..1b11ac0b5e --- /dev/null +++ b/ARMeilleure/Translation/PTC/IPtcLoadState.cs @@ -0,0 +1,10 @@ +using System; + +namespace ARMeilleure.Translation.PTC +{ + public interface IPtcLoadState + { + event Action PtcStateChanged; + void Continue(); + } +} \ No newline at end of file diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index c2d358c1ed..f99d6e5168 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -22,7 +22,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter; namespace ARMeilleure.Translation.PTC { - public static class Ptc + class Ptc : IPtcLoadState { private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; @@ -35,45 +35,49 @@ namespace ARMeilleure.Translation.PTC private const string TitleIdTextDefault = "0000000000000000"; private const string DisplayVersionDefault = "0"; - internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1); - internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2); - internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3); + public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1); + public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2); + public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3); private const byte FillingByte = 0x00; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; + public PtcProfiler Profiler { get; } + // Carriers. - private static MemoryStream _infosStream; - private static List _codesList; - private static MemoryStream _relocsStream; - private static MemoryStream _unwindInfosStream; + private MemoryStream _infosStream; + private List _codesList; + private MemoryStream _relocsStream; + private MemoryStream _unwindInfosStream; - private static readonly ulong _outerHeaderMagic; - private static readonly ulong _innerHeaderMagic; + private readonly ulong _outerHeaderMagic; + private readonly ulong _innerHeaderMagic; - private static readonly ManualResetEvent _waitEvent; + private readonly ManualResetEvent _waitEvent; - private static readonly object _lock; + private readonly object _lock; - private static bool _disposed; + private bool _disposed; - internal static string TitleIdText { get; private set; } - internal static string DisplayVersion { get; private set; } + public string TitleIdText { get; private set; } + public string DisplayVersion { get; private set; } - private static MemoryManagerMode _memoryMode; + private MemoryManagerType _memoryMode; - internal static string CachePathActual { get; private set; } - internal static string CachePathBackup { get; private set; } + public string CachePathActual { get; private set; } + public string CachePathBackup { get; private set; } - internal static PtcState State { get; private set; } + public PtcState State { get; private set; } // Progress reporting helpers. - private static volatile int _translateCount; - private static volatile int _translateTotalCount; - public static event Action PtcStateChanged; + private volatile int _translateCount; + private volatile int _translateTotalCount; + public event Action PtcStateChanged; - static Ptc() + public Ptc() { + Profiler = new PtcProfiler(this); + InitializeCarriers(); _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); @@ -94,12 +98,12 @@ namespace ARMeilleure.Translation.PTC Disable(); } - public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode) + public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode) { Wait(); - PtcProfiler.Wait(); - PtcProfiler.ClearEntries(); + Profiler.Wait(); + Profiler.ClearEntries(); Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled})."); @@ -137,12 +141,12 @@ namespace ARMeilleure.Translation.PTC CachePathBackup = Path.Combine(workPathBackup, DisplayVersion); PreLoad(); - PtcProfiler.PreLoad(); + Profiler.PreLoad(); Enable(); } - private static void InitializeCarriers() + private void InitializeCarriers() { _infosStream = new MemoryStream(); _codesList = new List(); @@ -150,7 +154,7 @@ namespace ARMeilleure.Translation.PTC _unwindInfosStream = new MemoryStream(); } - private static void DisposeCarriers() + private void DisposeCarriers() { _infosStream.Dispose(); _codesList.Clear(); @@ -158,12 +162,12 @@ namespace ARMeilleure.Translation.PTC _unwindInfosStream.Dispose(); } - private static bool AreCarriersEmpty() + private bool AreCarriersEmpty() { return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L; } - private static void ResetCarriersIfNeeded() + private void ResetCarriersIfNeeded() { if (AreCarriersEmpty()) { @@ -175,7 +179,7 @@ namespace ARMeilleure.Translation.PTC InitializeCarriers(); } - private static void PreLoad() + private void PreLoad() { string fileNameActual = string.Concat(CachePathActual, ".cache"); string fileNameBackup = string.Concat(CachePathBackup, ".cache"); @@ -199,7 +203,7 @@ namespace ARMeilleure.Translation.PTC } } - private static unsafe bool Load(string fileName, bool isBackup) + private unsafe bool Load(string fileName, bool isBackup) { using (FileStream compressedStream = new(fileName, FileMode.Open)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) @@ -376,12 +380,12 @@ namespace ARMeilleure.Translation.PTC return true; } - private static void InvalidateCompressedStream(FileStream compressedStream) + private void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); } - private static void PreSave() + private void PreSave() { _waitEvent.Reset(); @@ -409,7 +413,7 @@ namespace ARMeilleure.Translation.PTC _waitEvent.Set(); } - private static unsafe void Save(string fileName) + private unsafe void Save(string fileName) { int translatedFuncsCount; @@ -517,7 +521,7 @@ namespace ARMeilleure.Translation.PTC } } - internal static void LoadTranslations(Translator translator) + public void LoadTranslations(Translator translator) { if (AreCarriersEmpty()) { @@ -550,7 +554,7 @@ namespace ARMeilleure.Translation.PTC bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize); - if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) + if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) { infoEntry.Stubbed = true; infoEntry.CodeLength = 0; @@ -601,38 +605,38 @@ namespace ARMeilleure.Translation.PTC Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded"); } - private static int GetEntriesCount() + private int GetEntriesCount() { return _codesList.Count; } [Conditional("DEBUG")] - private static void SkipCode(int index, int codeLength) + private void SkipCode(int index, int codeLength) { Debug.Assert(_codesList[index].Length == 0); Debug.Assert(codeLength == 0); } - private static void SkipReloc(int relocEntriesCount) + private void SkipReloc(int relocEntriesCount) { _relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current); } - private static void SkipUnwindInfo(BinaryReader unwindInfosReader) + private void SkipUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); _unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current); } - private static byte[] ReadCode(int index, int codeLength) + private byte[] ReadCode(int index, int codeLength) { Debug.Assert(_codesList[index].Length == codeLength); return _codesList[index]; } - private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) + private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) { RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount]; @@ -648,7 +652,7 @@ namespace ARMeilleure.Translation.PTC return relocEntries; } - private static void PatchCode(Translator translator, Span code, RelocEntry[] relocEntries, out Counter callCounter) + private void PatchCode(Translator translator, Span code, RelocEntry[] relocEntries, out Counter callCounter) { callCounter = null; @@ -702,7 +706,7 @@ namespace ARMeilleure.Translation.PTC } } - private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) + private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); @@ -723,7 +727,7 @@ namespace ARMeilleure.Translation.PTC return new UnwindInfo(pushEntries, prologueSize); } - private static TranslatedFunction FastTranslate( + private TranslatedFunction FastTranslate( byte[] code, Counter callCounter, ulong guestSize, @@ -736,19 +740,19 @@ namespace ARMeilleure.Translation.PTC return new TranslatedFunction(gFunc, callCounter, guestSize, highCq); } - private static void UpdateInfo(InfoEntry infoEntry) + private void UpdateInfo(InfoEntry infoEntry) { _infosStream.Seek(-Unsafe.SizeOf(), SeekOrigin.Current); SerializeStructure(_infosStream, infoEntry); } - private static void StubCode(int index) + private void StubCode(int index) { _codesList[index] = Array.Empty(); } - private static void StubReloc(int relocEntriesCount) + private void StubReloc(int relocEntriesCount) { for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++) { @@ -756,7 +760,7 @@ namespace ARMeilleure.Translation.PTC } } - private static void StubUnwindInfo(BinaryReader unwindInfosReader) + private void StubUnwindInfo(BinaryReader unwindInfosReader) { int pushEntriesLength = unwindInfosReader.ReadInt32(); @@ -766,9 +770,9 @@ namespace ARMeilleure.Translation.PTC } } - internal static void MakeAndSaveTranslations(Translator translator) + public void MakeAndSaveTranslations(Translator translator) { - var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions); + var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions); _translateCount = 0; _translateTotalCount = profiledFuncsToTranslate.Count; @@ -811,7 +815,7 @@ namespace ARMeilleure.Translation.PTC { ulong address = item.address; - Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); + Debug.Assert(Profiler.IsAddressInStaticCodeRange(address)); TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq); @@ -861,7 +865,7 @@ namespace ARMeilleure.Translation.PTC preSaveThread.Start(); } - private static void ReportProgress(object state) + private void ReportProgress(object state) { const int refreshRate = 50; // ms. @@ -882,12 +886,12 @@ namespace ARMeilleure.Translation.PTC while (!endEvent.WaitOne(refreshRate)); } - internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) + public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) { return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); } - internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc) + public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc) { lock (_lock) { @@ -936,12 +940,12 @@ namespace ARMeilleure.Translation.PTC } } - private static void WriteCode(ReadOnlySpan code) + private void WriteCode(ReadOnlySpan code) { _codesList.Add(code.ToArray()); } - internal static bool GetEndianness() + public static bool GetEndianness() { return BitConverter.IsLittleEndian; } @@ -955,7 +959,7 @@ namespace ARMeilleure.Translation.PTC (uint)HardwareCapabilities.FeatureInfo7Ecx); } - private static byte GetMemoryManagerMode() + private byte GetMemoryManagerMode() { return (byte)_memoryMode; } @@ -1050,12 +1054,12 @@ namespace ARMeilleure.Translation.PTC public int RelocEntriesCount; } - private static void Enable() + private void Enable() { State = PtcState.Enabled; } - public static void Continue() + public void Continue() { if (State == PtcState.Enabled) { @@ -1063,7 +1067,7 @@ namespace ARMeilleure.Translation.PTC } } - public static void Close() + public void Close() { if (State == PtcState.Enabled || State == PtcState.Continuing) @@ -1072,17 +1076,17 @@ namespace ARMeilleure.Translation.PTC } } - internal static void Disable() + public void Disable() { State = PtcState.Disabled; } - private static void Wait() + private void Wait() { _waitEvent.WaitOne(); } - public static void Dispose() + public void Dispose() { if (!_disposed) { diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs index bb70da8d07..0d5546283b 100644 --- a/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -16,7 +16,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter; namespace ARMeilleure.Translation.PTC { - public static class PtcProfiler + class PtcProfiler { private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; @@ -26,27 +26,31 @@ namespace ARMeilleure.Translation.PTC private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; - private static readonly System.Timers.Timer _timer; + private readonly Ptc _ptc; - private static readonly ulong _outerHeaderMagic; + private readonly System.Timers.Timer _timer; - private static readonly ManualResetEvent _waitEvent; + private readonly ulong _outerHeaderMagic; - private static readonly object _lock; + private readonly ManualResetEvent _waitEvent; - private static bool _disposed; + private readonly object _lock; - private static Hash128 _lastHash; + private bool _disposed; - internal static Dictionary ProfiledFuncs { get; private set; } + private Hash128 _lastHash; - internal static bool Enabled { get; private set; } + public Dictionary ProfiledFuncs { get; private set; } - public static ulong StaticCodeStart { internal get; set; } - public static ulong StaticCodeSize { internal get; set; } + public bool Enabled { get; private set; } - static PtcProfiler() + public ulong StaticCodeStart { get; set; } + public ulong StaticCodeSize { get; set; } + + public PtcProfiler(Ptc ptc) { + _ptc = ptc; + _timer = new System.Timers.Timer((double)SaveInterval * 1000d); _timer.Elapsed += PreSave; @@ -63,7 +67,7 @@ namespace ARMeilleure.Translation.PTC Enabled = false; } - internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq) + public void AddEntry(ulong address, ExecutionMode mode, bool highCq) { if (IsAddressInStaticCodeRange(address)) { @@ -76,7 +80,7 @@ namespace ARMeilleure.Translation.PTC } } - internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq) + public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq) { if (IsAddressInStaticCodeRange(address)) { @@ -91,12 +95,12 @@ namespace ARMeilleure.Translation.PTC } } - internal static bool IsAddressInStaticCodeRange(ulong address) + public bool IsAddressInStaticCodeRange(ulong address) { return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; } - internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache funcs) + public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache funcs) { var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>(); @@ -111,18 +115,18 @@ namespace ARMeilleure.Translation.PTC return profiledFuncsToTranslate; } - internal static void ClearEntries() + public void ClearEntries() { ProfiledFuncs.Clear(); ProfiledFuncs.TrimExcess(); } - internal static void PreLoad() + public void PreLoad() { _lastHash = default; - string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); - string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); + string fileNameActual = string.Concat(_ptc.CachePathActual, ".info"); + string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info"); FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoBackup = new FileInfo(fileNameBackup); @@ -143,7 +147,7 @@ namespace ARMeilleure.Translation.PTC } } - private static bool Load(string fileName, bool isBackup) + private bool Load(string fileName, bool isBackup) { using (FileStream compressedStream = new(fileName, FileMode.Open)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) @@ -228,22 +232,22 @@ namespace ARMeilleure.Translation.PTC return DeserializeDictionary(stream, (stream) => DeserializeStructure(stream)); } - private static ReadOnlySpan GetReadOnlySpan(MemoryStream memoryStream) + private ReadOnlySpan GetReadOnlySpan(MemoryStream memoryStream) { return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); } - private static void InvalidateCompressedStream(FileStream compressedStream) + private void InvalidateCompressedStream(FileStream compressedStream) { compressedStream.SetLength(0L); } - private static void PreSave(object source, System.Timers.ElapsedEventArgs e) + private void PreSave(object source, System.Timers.ElapsedEventArgs e) { _waitEvent.Reset(); - string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); - string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); + string fileNameActual = string.Concat(_ptc.CachePathActual, ".info"); + string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info"); FileInfo fileInfoActual = new FileInfo(fileNameActual); @@ -257,7 +261,7 @@ namespace ARMeilleure.Translation.PTC _waitEvent.Set(); } - private static void Save(string fileName) + private void Save(string fileName) { int profiledFuncsCount; @@ -329,7 +333,7 @@ namespace ARMeilleure.Translation.PTC } } - private static void Serialize(Stream stream, Dictionary profiledFuncs) + private void Serialize(Stream stream, Dictionary profiledFuncs) { SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); } @@ -361,7 +365,7 @@ namespace ARMeilleure.Translation.PTC } [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)] - internal struct FuncProfile + public struct FuncProfile { public ExecutionMode Mode; public bool HighCq; @@ -373,10 +377,10 @@ namespace ARMeilleure.Translation.PTC } } - internal static void Start() + public void Start() { - if (Ptc.State == PtcState.Enabled || - Ptc.State == PtcState.Continuing) + if (_ptc.State == PtcState.Enabled || + _ptc.State == PtcState.Continuing) { Enabled = true; @@ -384,7 +388,7 @@ namespace ARMeilleure.Translation.PTC } } - public static void Stop() + public void Stop() { Enabled = false; @@ -394,12 +398,12 @@ namespace ARMeilleure.Translation.PTC } } - internal static void Wait() + public void Wait() { _waitEvent.WaitOne(); } - public static void Dispose() + public void Dispose() { if (!_disposed) { diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index 2edbe4011a..77ccdaeab3 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -44,6 +44,8 @@ namespace ARMeilleure.Translation private readonly IJitMemoryAllocator _allocator; private readonly ConcurrentQueue> _oldFuncs; + private readonly Ptc _ptc; + internal TranslatorCache Functions { get; } internal AddressTable FunctionTable { get; } internal EntryTable CountTable { get; } @@ -63,6 +65,8 @@ namespace ARMeilleure.Translation _oldFuncs = new ConcurrentQueue>(); + _ptc = new Ptc(); + Queue = new TranslatorQueue(); JitCache.Initialize(allocator); @@ -80,22 +84,37 @@ namespace ARMeilleure.Translation } } + public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + { + _ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type); + return _ptc; + } + + public void PrepareCodeRange(ulong address, ulong size) + { + if (_ptc.Profiler.StaticCodeSize == 0) + { + _ptc.Profiler.StaticCodeStart = address; + _ptc.Profiler.StaticCodeSize = size; + } + } + public void Execute(State.ExecutionContext context, ulong address) { if (Interlocked.Increment(ref _threadCount) == 1) { IsReadyForTranslation.WaitOne(); - if (Ptc.State == PtcState.Enabled) + if (_ptc.State == PtcState.Enabled) { Debug.Assert(Functions.Count == 0); - Ptc.LoadTranslations(this); - Ptc.MakeAndSaveTranslations(this); + _ptc.LoadTranslations(this); + _ptc.MakeAndSaveTranslations(this); } - PtcProfiler.Start(); + _ptc.Profiler.Start(); - Ptc.Disable(); + _ptc.Disable(); // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht // etc). All threads are normal priority except from the last, which just fills as much of the last core @@ -148,6 +167,12 @@ namespace ARMeilleure.Translation Stubs.Dispose(); FunctionTable.Dispose(); CountTable.Dispose(); + + _ptc.Close(); + _ptc.Profiler.Stop(); + + _ptc.Dispose(); + _ptc.Profiler.Dispose(); } } @@ -189,9 +214,9 @@ namespace ARMeilleure.Translation func = oldFunc; } - if (PtcProfiler.Enabled) + if (_ptc.Profiler.Enabled) { - PtcProfiler.AddEntry(address, mode, highCq: false); + _ptc.Profiler.AddEntry(address, mode, highCq: false); } RegisterFunction(address, func); @@ -217,6 +242,7 @@ namespace ARMeilleure.Translation Stubs, address, highCq, + _ptc.State != PtcState.Disabled, mode: Aarch32Mode.User); Logger.StartPass(PassName.Decoding); @@ -262,7 +288,7 @@ namespace ARMeilleure.Translation { Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize); - Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); + _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); } GuestFunction func = compiledFunc.Map(); @@ -284,9 +310,9 @@ namespace ARMeilleure.Translation return func; }); - if (PtcProfiler.Enabled) + if (_ptc.Profiler.Enabled) { - PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true); + _ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true); } RegisterFunction(request.Address, func); diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 0baa94c3b1..2cf53ef691 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using Avalonia.Input; using Avalonia.Threading; using LibHac.Tools.FsSystem; @@ -280,7 +279,7 @@ namespace Ryujinx.Ava _parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; }); - _parent.ViewModel.HandleShaderProgress(Device); + _parent.ViewModel.SetUiProgressHandlers(Device); Renderer.SizeChanged += Window_SizeChanged; @@ -357,8 +356,6 @@ namespace Ryujinx.Ava DisplaySleep.Restore(); - Ptc.Close(); - PtcProfiler.Stop(); NpadManager.Dispose(); TouchScreenManager.Dispose(); Device.Dispose(); @@ -949,7 +946,7 @@ namespace Ryujinx.Ava if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen) { - Ptc.Continue(); + Device.Application.DiskCacheLoadState?.Cancel(); } }); } diff --git a/Ryujinx.Ava/Program.cs b/Ryujinx.Ava/Program.cs index 142d7820be..836801c88f 100644 --- a/Ryujinx.Ava/Program.cs +++ b/Ryujinx.Ava/Program.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using Avalonia; using Avalonia.Threading; using Ryujinx.Ava.UI.Windows; @@ -197,9 +196,6 @@ namespace Ryujinx.Ava private static void ProcessUnhandledException(Exception ex, bool isTerminating) { - Ptc.Close(); - PtcProfiler.Stop(); - string message = $"Unhandled exception caught: {ex}"; Logger.Error?.PrintMsg(LogClass.Application, message); @@ -219,9 +215,6 @@ namespace Ryujinx.Ava { DiscordIntegrationModule.Exit(); - Ptc.Dispose(); - PtcProfiler.Dispose(); - Logger.Shutdown(); } } diff --git a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 878af3f810..953f8562cf 100644 --- a/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using Avalonia; using Avalonia.Controls; using Avalonia.Input; @@ -18,6 +17,7 @@ using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Cpu; using Ryujinx.HLE; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; @@ -107,9 +107,6 @@ namespace Ryujinx.Ava.UI.ViewModels { ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; - - Ptc.PtcStateChanged -= ProgressHandler; - Ptc.PtcStateChanged += ProgressHandler; } public string SearchText @@ -436,7 +433,7 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } - + public bool ShowMenuAndStatusBar { get => _showMenuAndStatusBar; @@ -745,8 +742,14 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void HandleShaderProgress(Switch emulationContext) + public void SetUiProgressHandlers(Switch emulationContext) { + if (emulationContext.Application.DiskCacheLoadState != null) + { + emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } + emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; } @@ -1033,16 +1036,16 @@ namespace Ryujinx.Ava.UI.ViewModels switch (state) { - case PtcLoadingState ptcState: + case LoadState ptcState: CacheLoadStatus = $"{current} / {total}"; switch (ptcState) { - case PtcLoadingState.Start: - case PtcLoadingState.Loading: + case LoadState.Unloaded: + case LoadState.Loading: LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingPPTC]; IsLoadingIndeterminate = false; break; - case PtcLoadingState.Loaded: + case LoadState.Loaded: LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); IsLoadingIndeterminate = true; CacheLoadStatus = ""; @@ -1166,7 +1169,7 @@ namespace Ryujinx.Ava.UI.ViewModels DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1")); // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionMessage], selection.TitleName), LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo], diff --git a/Ryujinx.Cpu/ICpuContext.cs b/Ryujinx.Cpu/ICpuContext.cs index 4a73a83384..80916d1ca7 100644 --- a/Ryujinx.Cpu/ICpuContext.cs +++ b/Ryujinx.Cpu/ICpuContext.cs @@ -35,5 +35,27 @@ namespace Ryujinx.Cpu /// Address of the region to be invalidated /// Size of the region to be invalidated void InvalidateCacheRegion(ulong address, ulong size); + + /// + /// Loads cached code from disk for a given application. + /// + /// + /// If the execution engine is recompiling guest code, this can be used to load cached code from disk. + /// + /// Title ID of the application in padded hex form + /// Version of the application + /// True if the cache should be loaded from disk if it exists, false otherwise + /// Disk cache load progress reporter and manager + IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled); + + /// + /// Indicates that code has been loaded into guest memory, and that it might be executed in the future. + /// + /// + /// Some execution engines might use this information to cache recompiled code on disk or to ensure it can be executed. + /// + /// CPU virtual address where the code starts + /// Size of the code range in bytes + void PrepareCodeRange(ulong address, ulong size); } } diff --git a/Ryujinx.Cpu/IDiskCacheState.cs b/Ryujinx.Cpu/IDiskCacheState.cs new file mode 100644 index 0000000000..61bbdf9249 --- /dev/null +++ b/Ryujinx.Cpu/IDiskCacheState.cs @@ -0,0 +1,20 @@ +using System; + +namespace Ryujinx.Cpu +{ + /// + /// Disk cache load state report and management interface. + /// + public interface IDiskCacheLoadState + { + /// + /// Event used to report the cache load progress. + /// + event Action StateChanged; + + /// + /// Cancels the disk cache load process. + /// + void Cancel(); + } +} diff --git a/Ryujinx.Cpu/Jit/JitCpuContext.cs b/Ryujinx.Cpu/Jit/JitCpuContext.cs index d6892ea759..02465a0b3a 100644 --- a/Ryujinx.Cpu/Jit/JitCpuContext.cs +++ b/Ryujinx.Cpu/Jit/JitCpuContext.cs @@ -37,5 +37,17 @@ namespace Ryujinx.Cpu.Jit { _translator.InvalidateJitCacheRegion(address, size); } + + /// + public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + { + return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled)); + } + + /// + public void PrepareCodeRange(ulong address, ulong size) + { + _translator.PrepareCodeRange(address, size); + } } } diff --git a/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs b/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs new file mode 100644 index 0000000000..7a4b670b38 --- /dev/null +++ b/Ryujinx.Cpu/Jit/JitDiskCacheLoadState.cs @@ -0,0 +1,38 @@ +using ARMeilleure.Translation.PTC; +using System; + +namespace Ryujinx.Cpu.Jit +{ + public class JitDiskCacheLoadState : IDiskCacheLoadState + { + /// + public event Action StateChanged; + + private readonly IPtcLoadState _loadState; + + public JitDiskCacheLoadState(IPtcLoadState loadState) + { + loadState.PtcStateChanged += LoadStateChanged; + _loadState = loadState; + } + + private void LoadStateChanged(PtcLoadingState newState, int current, int total) + { + LoadState state = newState switch + { + PtcLoadingState.Start => LoadState.Unloaded, + PtcLoadingState.Loading => LoadState.Loading, + PtcLoadingState.Loaded => LoadState.Loaded, + _ => throw new ArgumentException($"Invalid load state \"{newState}\".") + }; + + StateChanged?.Invoke(state, current, total); + } + + /// + public void Cancel() + { + _loadState.Continue(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Cpu/LoadState.cs b/Ryujinx.Cpu/LoadState.cs new file mode 100644 index 0000000000..1f2e1ae8cc --- /dev/null +++ b/Ryujinx.Cpu/LoadState.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Cpu +{ + /// + /// Load state. + /// + public enum LoadState + { + Unloaded, + Loading, + Loaded + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 61fcd0c35e..06281b497e 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using LibHac; using LibHac.Account; using LibHac.Common; @@ -14,8 +13,8 @@ using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Cpu; using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.Memory; using System; @@ -67,6 +66,8 @@ namespace Ryujinx.HLE.HOS public string TitleIdText => TitleId.ToString("x16"); + public IDiskCacheLoadState DiskCacheLoadState { get; private set; } + public ApplicationLoader(Switch device) { _device = device; @@ -94,7 +95,7 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId)); } - LoadExeFs(codeFs, metaData); + LoadExeFs(codeFs, string.Empty, metaData); } public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) @@ -302,12 +303,6 @@ namespace Ryujinx.HLE.HOS public void LoadServiceNca(string ncaFile) { - // Disable PPTC here as it does not support multiple processes running. - // TODO: This should be eventually removed and it should stop using global state and - // instead manage the cache per process. - Ptc.Close(); - PtcProfiler.Stop(); - FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); @@ -369,16 +364,12 @@ namespace Ryujinx.HLE.HOS // Collect the nsos, ignoring ones that aren't used. NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode; - - if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible)) - { - memoryManagerMode = MemoryManagerMode.SoftwarePageTable; - } + string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString; + bool usePtc = _device.System.EnablePtc; metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false); - ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs); + ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false); + ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16"); bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; @@ -477,9 +468,11 @@ namespace Ryujinx.HLE.HOS _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); + string displayVersion = string.Empty; + if (controlNca != null) { - ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref _displayVersion); + ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion); } else { @@ -493,9 +486,11 @@ namespace Ryujinx.HLE.HOS string dummyTitleName = ""; BlitStruct dummyControl = new BlitStruct(1); - ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref _displayVersion); + ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion); } + _displayVersion = displayVersion; + if (dataStorage == null) { Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); @@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); } - LoadExeFs(codeFs, metaData); + LoadExeFs(codeFs, displayVersion, metaData); Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); } @@ -584,7 +579,7 @@ namespace Ryujinx.HLE.HOS } } - private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false) + private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false) { if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) { @@ -649,23 +644,23 @@ namespace Ryujinx.HLE.HOS memoryManagerMode = MemoryManagerMode.SoftwarePageTable; } - Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode); - // We allow it for nx-hbloader because it can be used to launch homebrew. bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew; metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit); - ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: programs); + ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit); + ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs); - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); + DiskCacheLoadState = result.DiskCacheLoadState; + + _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); } public void LoadProgram(string filePath) { MetaLoader metaData = GetDefaultNpdm(); metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); - ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: true); + ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true); bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; @@ -761,9 +756,11 @@ namespace Ryujinx.HLE.HOS Graphics.Gpu.GraphicsConfig.TitleId = null; _device.Gpu.HostInitalized.Set(); - ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: executable); + ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable); - _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); + DiskCacheLoadState = result.DiskCacheLoadState; + + _device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine); } private MetaLoader GetDefaultNpdm() diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/Ryujinx.HLE/HOS/ArmProcessContext.cs index 072df0b612..6338edc1e6 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -6,7 +6,17 @@ using Ryujinx.Memory; namespace Ryujinx.HLE.HOS { - class ArmProcessContext : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager + interface IArmProcessContext : IProcessContext + { + IDiskCacheLoadState Initialize( + string titleIdText, + string displayVersion, + bool diskCacheEnabled, + ulong codeAddress, + ulong codeSize); + } + + class ArmProcessContext : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager { private readonly ulong _pid; private readonly GpuContext _gpuContext; @@ -40,6 +50,17 @@ namespace Ryujinx.HLE.HOS _cpuContext.Execute(context, codeAddress); } + public IDiskCacheLoadState Initialize( + string titleIdText, + string displayVersion, + bool diskCacheEnabled, + ulong codeAddress, + ulong codeSize) + { + _cpuContext.PrepareCodeRange(codeAddress, codeSize); + return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled); + } + public void InvalidateCacheRegion(ulong address, ulong size) { _cpuContext.InvalidateCacheRegion(address, size); diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 7d1c4e1d6e..5ecaf38e90 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -13,11 +13,30 @@ namespace Ryujinx.HLE.HOS { private readonly ICpuEngine _cpuEngine; private readonly GpuContext _gpu; + private readonly string _titleIdText; + private readonly string _displayVersion; + private readonly bool _diskCacheEnabled; + private readonly ulong _codeAddress; + private readonly ulong _codeSize; - public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu) + public IDiskCacheLoadState DiskCacheLoadState { get; private set; } + + public ArmProcessContextFactory( + ICpuEngine cpuEngine, + GpuContext gpu, + string titleIdText, + string displayVersion, + bool diskCacheEnabled, + ulong codeAddress, + ulong codeSize) { _cpuEngine = cpuEngine; _gpu = gpu; + _titleIdText = titleIdText; + _displayVersion = displayVersion; + _diskCacheEnabled = diskCacheEnabled; + _codeAddress = codeAddress; + _codeSize = codeSize; } public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit) @@ -29,21 +48,29 @@ namespace Ryujinx.HLE.HOS mode = MemoryManagerMode.SoftwarePageTable; } + IArmProcessContext processContext; + switch (mode) { case MemoryManagerMode.SoftwarePageTable: var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); - return new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManager, for64Bit); + processContext = new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManager, for64Bit); + break; case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMappedUnsafe: bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler); - return new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); + processContext = new ArmProcessContext(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); + break; default: throw new ArgumentOutOfRangeException(); } + + DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize); + + return processContext; } } } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index b422fef754..09e1ac31c4 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/Ryujinx.HLE/HOS/ProgramLoader.cs @@ -1,9 +1,9 @@ -using ARMeilleure.Translation.PTC; using LibHac.Loader; using LibHac.Ncm; using LibHac.Util; using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Memory; @@ -21,16 +21,40 @@ namespace Ryujinx.HLE.HOS { public string Name; public ulong ProgramId; - public bool AllowCodeMemoryForJit; + public readonly string TitleIdText; + public readonly string DisplayVersion; + public readonly bool DiskCacheEnabled; + public readonly bool AllowCodeMemoryForJit; - public ProgramInfo(in Npdm npdm, bool allowCodeMemoryForJit) + public ProgramInfo(in Npdm npdm, string displayVersion, bool diskCacheEnabled, bool allowCodeMemoryForJit) { + ulong programId = npdm.Aci.Value.ProgramId.Value; + Name = StringUtils.Utf8ZToString(npdm.Meta.Value.ProgramName); - ProgramId = npdm.Aci.Value.ProgramId.Value; + ProgramId = programId; + TitleIdText = programId.ToString("x16"); + DisplayVersion = displayVersion; + DiskCacheEnabled = diskCacheEnabled; AllowCodeMemoryForJit = allowCodeMemoryForJit; } } + struct ProgramLoadResult + { + public static ProgramLoadResult Failed => new ProgramLoadResult(false, null, null); + + public readonly bool Success; + public readonly ProcessTamperInfo TamperInfo; + public readonly IDiskCacheLoadState DiskCacheLoadState; + + public ProgramLoadResult(bool success, ProcessTamperInfo tamperInfo, IDiskCacheLoadState diskCacheLoadState) + { + Success = success; + TamperInfo = tamperInfo; + DiskCacheLoadState = diskCacheLoadState; + } + } + static class ProgramLoader { private const bool AslrEnabled = true; @@ -102,7 +126,14 @@ namespace Ryujinx.HLE.HOS KProcess process = new KProcess(context); - var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu); + var processContextFactory = new ArmProcessContextFactory( + context.Device.System.CpuEngine, + context.Device.Gpu, + string.Empty, + string.Empty, + false, + codeAddress, + codeSize); result = process.InitializeKip( creationInfo, @@ -144,9 +175,8 @@ namespace Ryujinx.HLE.HOS return true; } - public static bool LoadNsos( + public static ProgramLoadResult LoadNsos( KernelContext context, - out ProcessTamperInfo tamperInfo, MetaLoader metaData, ProgramInfo programInfo, byte[] arguments = null, @@ -156,8 +186,7 @@ namespace Ryujinx.HLE.HOS if (rc.IsFailure()) { - tamperInfo = null; - return false; + return ProgramLoadResult.Failed; } ref readonly var meta = ref npdm.Meta.Value; @@ -212,9 +241,6 @@ namespace Ryujinx.HLE.HOS } } - PtcProfiler.StaticCodeStart = codeStart; - PtcProfiler.StaticCodeSize = (ulong)codeSize; - int codePagesCount = (int)(codeSize / KPageTableBase.PageSize); int personalMmHeapPagesCount = (int)(meta.SystemResourceSize / KPageTableBase.PageSize); @@ -263,9 +289,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed setting resource limit values."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } KProcess process = new KProcess(context, programInfo.AllowCodeMemoryForJit); @@ -276,12 +300,17 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization failed due to invalid ACID flags."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } - var processContextFactory = new ArmProcessContextFactory(context.Device.System.CpuEngine, context.Device.Gpu); + var processContextFactory = new ArmProcessContextFactory( + context.Device.System.CpuEngine, + context.Device.Gpu, + programInfo.TitleIdText, + programInfo.DisplayVersion, + programInfo.DiskCacheEnabled, + codeStart, + codeSize); result = process.Initialize( creationInfo, @@ -294,9 +323,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } for (int index = 0; index < executables.Length; index++) @@ -309,9 +336,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process initialization returned error \"{result}\"."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } } @@ -323,9 +348,7 @@ namespace Ryujinx.HLE.HOS { Logger.Error?.Print(LogClass.Loader, $"Process start returned error \"{result}\"."); - tamperInfo = null; - - return false; + return ProgramLoadResult.Failed; } context.Processes.TryAdd(process.Pid, process); @@ -333,10 +356,15 @@ namespace Ryujinx.HLE.HOS // Keep the build ids because the tamper machine uses them to know which process to associate a // tamper to and also keep the starting address of each executable inside a process because some // memory modifications are relative to this address. - tamperInfo = new ProcessTamperInfo(process, buildIds, nsoBase, process.MemoryManager.HeapRegionStart, - process.MemoryManager.AliasRegionStart, process.MemoryManager.CodeRegionStart); + ProcessTamperInfo tamperInfo = new ProcessTamperInfo( + process, + buildIds, + nsoBase, + process.MemoryManager.HeapRegionStart, + process.MemoryManager.AliasRegionStart, + process.MemoryManager.CodeRegionStart); - return true; + return new ProgramLoadResult(true, tamperInfo, processContextFactory.DiskCacheLoadState); } private static Result LoadIntoMemory(KProcess process, IExecutable image, ulong baseAddress) diff --git a/Ryujinx.Headless.SDL2/Program.cs b/Ryujinx.Headless.SDL2/Program.cs index 4a2ba99de6..b0c29e561c 100644 --- a/Ryujinx.Headless.SDL2/Program.cs +++ b/Ryujinx.Headless.SDL2/Program.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using CommandLine; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; @@ -12,6 +11,7 @@ using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; using Ryujinx.Common.Utilities; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.Gpu; @@ -447,8 +447,11 @@ namespace Ryujinx.Headless.SDL2 private static void SetupProgressHandler() { - Ptc.PtcStateChanged -= ProgressHandler; - Ptc.PtcStateChanged += ProgressHandler; + if (_emulationContext.Application.DiskCacheLoadState != null) + { + _emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; @@ -460,7 +463,7 @@ namespace Ryujinx.Headless.SDL2 switch (state) { - case PtcLoadingState ptcState: + case LoadState ptcState: label = $"PTC : {current}/{total}"; break; case ShaderCacheState shaderCacheState: @@ -563,9 +566,6 @@ namespace Ryujinx.Headless.SDL2 _window.Execute(); - Ptc.Close(); - PtcProfiler.Stop(); - _emulationContext.Dispose(); _window.Dispose(); diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index e27c4ae944..787a8ad5fe 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -1,4 +1,3 @@ -using ARMeilleure.Translation.PTC; using Gtk; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -308,9 +307,6 @@ namespace Ryujinx private static void ProcessUnhandledException(Exception ex, bool isTerminating) { - Ptc.Close(); - PtcProfiler.Stop(); - string message = $"Unhandled exception caught: {ex}"; Logger.Error?.PrintMsg(LogClass.Application, message); @@ -330,9 +326,6 @@ namespace Ryujinx { DiscordIntegrationModule.Exit(); - Ptc.Dispose(); - PtcProfiler.Dispose(); - Logger.Shutdown(); } } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 0e7e4d6255..495f665198 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using Gtk; using LibHac.Common; using LibHac.Common.Keys; @@ -16,6 +15,7 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.OpenGL; @@ -46,7 +46,6 @@ using System.Threading; using System.Threading.Tasks; using GUI = Gtk.Builder.ObjectAttribute; -using PtcLoadingState = ARMeilleure.Translation.PTC.PtcLoadingState; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; namespace Ryujinx.Ui @@ -588,8 +587,11 @@ namespace Ryujinx.Ui private void SetupProgressUiHandlers() { - Ptc.PtcStateChanged -= ProgressHandler; - Ptc.PtcStateChanged += ProgressHandler; + if (_emulationContext.Application.DiskCacheLoadState != null) + { + _emulationContext.Application.DiskCacheLoadState.StateChanged -= ProgressHandler; + _emulationContext.Application.DiskCacheLoadState.StateChanged += ProgressHandler; + } _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; @@ -602,8 +604,8 @@ namespace Ryujinx.Ui switch (state) { - case PtcLoadingState ptcState: - visible = ptcState != PtcLoadingState.Loaded; + case LoadState ptcState: + visible = ptcState != LoadState.Loaded; label = $"PTC : {current}/{total}"; break; case ShaderCacheLoadingState shaderCacheState: @@ -705,8 +707,6 @@ namespace Ryujinx.Ui UpdateGraphicsConfig(); - SetupProgressUiHandlers(); - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); bool isDirectory = Directory.Exists(path); @@ -841,6 +841,8 @@ namespace Ryujinx.Ui return; } + SetupProgressUiHandlers(); + _currentEmulatedGamePath = path; _deviceExitStatus.Reset(); @@ -967,9 +969,6 @@ namespace Ryujinx.Ui RendererWidget.Start(); - Ptc.Close(); - PtcProfiler.Stop(); - _emulationContext.Dispose(); _deviceExitStatus.Set(); diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 9dabe81736..8db023bec5 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -1,5 +1,4 @@ using ARMeilleure.Translation; -using ARMeilleure.Translation.PTC; using Gdk; using Gtk; using Ryujinx.Common; @@ -519,7 +518,7 @@ namespace Ryujinx.Ui _gpuCancellationTokenSource.Cancel(); _isStopped = true; - + if (_isActive) { _isActive = false; @@ -585,7 +584,7 @@ namespace Ryujinx.Ui { if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) { - Ptc.Continue(); + Device.Application.DiskCacheLoadState?.Cancel(); } } });