diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 125cd012b..45f0688bf 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -3,6 +3,7 @@ using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.Memory; using ARMeilleure.Native; using Ryujinx.Memory; +using Ryujinx.Common.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -15,52 +16,73 @@ namespace ARMeilleure.Translation.Cache static partial class JitCache { private static readonly int _pageSize = (int)MemoryBlock.GetPageSize(); - private static readonly int _pageMask = _pageSize - 1; + private static readonly int _pageMask = _pageSize - 4; private const int CodeAlignment = 4; // Bytes. - private const int CacheSize = 2047 * 1024 * 1024; - private const int CacheSizeIOS = 1024 * 1024 * 1024; + private const int CacheSize = 128 * 1024 * 1024; + private const int CacheSizeIOS = 128 * 1024 * 1024; private static ReservedRegion _jitRegion; private static JitCacheInvalidation _jitCacheInvalidator; - private static CacheMemoryAllocator _cacheAllocator; + private static List _cacheAllocators = []; private static readonly List _cacheEntries = new(); private static readonly object _lock = new(); private static bool _initialized; + private static readonly List _jitRegions = new(); + + private static int _activeRegionIndex = 0; + [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize); public static void Initialize(IJitMemoryAllocator allocator) { - if (_initialized) - { - return; - } - lock (_lock) { if (_initialized) { - return; + if (OperatingSystem.IsWindows()) + { + // JitUnwindWindows.RemoveFunctionTableHandler( + // _jitRegions[0].Pointer); + } + + for (int i = 0; i < _jitRegions.Count; i++) + { + _jitRegions[i].Dispose(); + } + + _jitRegions.Clear(); + _cacheAllocators.Clear(); + } + else + { + _initialized = true; } - _jitRegion = new ReservedRegion(allocator, (ulong)(OperatingSystem.IsIOS() ? CacheSizeIOS : CacheSize)); + _activeRegionIndex = 0; + + var firstRegion = new ReservedRegion(allocator, CacheSize); + _jitRegions.Add(firstRegion); + + CacheMemoryAllocator firstCacheAllocator = new(CacheSize); + _cacheAllocators.Add(firstCacheAllocator); if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS() && !OperatingSystem.IsIOS()) { _jitCacheInvalidator = new JitCacheInvalidation(allocator); } - _cacheAllocator = new CacheMemoryAllocator(CacheSize); - if (OperatingSystem.IsWindows()) { - JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize)); + JitUnwindWindows.InstallFunctionTableHandler( + firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize) + ); } _initialized = true; @@ -73,7 +95,9 @@ namespace ARMeilleure.Translation.Cache { while (_deferredRxProtect.TryDequeue(out var result)) { - ReprotectAsExecutable(result.funcOffset, result.length); + ReservedRegion targetRegion = _jitRegions[_activeRegionIndex]; + + ReprotectAsExecutable(targetRegion, result.funcOffset, result.length); } } @@ -87,7 +111,8 @@ namespace ARMeilleure.Translation.Cache int funcOffset = Allocate(code.Length, deferProtect); - IntPtr funcPtr = _jitRegion.Pointer + funcOffset; + ReservedRegion targetRegion = _jitRegions[_activeRegionIndex]; + IntPtr funcPtr = targetRegion.Pointer + funcOffset; if (OperatingSystem.IsIOS()) { @@ -98,8 +123,7 @@ namespace ARMeilleure.Translation.Cache } else { - ReprotectAsExecutable(funcOffset, code.Length); - + ReprotectAsExecutable(targetRegion, funcOffset, code.Length); JitSupportDarwinAot.Invalidate(funcPtr, (ulong)code.Length); } } @@ -115,9 +139,9 @@ namespace ARMeilleure.Translation.Cache } else { - ReprotectAsWritable(funcOffset, code.Length); + ReprotectAsWritable(targetRegion, funcOffset, code.Length); Marshal.Copy(code, 0, funcPtr, code.Length); - ReprotectAsExecutable(funcOffset, code.Length); + ReprotectAsExecutable(targetRegion, funcOffset, code.Length); if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { @@ -139,41 +163,50 @@ namespace ARMeilleure.Translation.Cache { if (OperatingSystem.IsIOS()) { - return; + // return; } lock (_lock) { - Debug.Assert(_initialized); - - int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64()); - - if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) + foreach (var region in _jitRegions) { - _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); - _cacheEntries.RemoveAt(entryIndex); + if (pointer.ToInt64() < region.Pointer.ToInt64() || + pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64()) + { + continue; + } + + int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64()); + + if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) + { + _cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size)); + _cacheEntries.RemoveAt(entryIndex); + } + + return; } } } - private static void ReprotectAsWritable(int offset, int size) + private static void ReprotectAsWritable(ReservedRegion region, int offset, int size) { int endOffs = offset + size; int regionStart = offset & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask; - _jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); + region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); } - private static void ReprotectAsExecutable(int offset, int size) + private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size) { int endOffs = offset + size; int regionStart = offset & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask; - _jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); + region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); } private static int Allocate(int codeSize, bool deferProtect = false) @@ -187,18 +220,33 @@ namespace ARMeilleure.Translation.Cache alignment = 0x4000; } - int allocOffset = _cacheAllocator.Allocate(ref codeSize, alignment); + int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(ref codeSize, alignment); - Console.WriteLine($"{allocOffset:x8}: {codeSize:x8} {alignment:x8}"); - - if (allocOffset < 0) + if (allocOffset >= 0) { - throw new OutOfMemoryException("JIT Cache exhausted."); + _jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + return allocOffset; } - _jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + int exhaustedRegion = _activeRegionIndex; + var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize); + _jitRegions.Add(newRegion); + _activeRegionIndex = _jitRegions.Count - 1; - return allocOffset; + int newRegionNumber = _activeRegionIndex; + + Logger.Info?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize)} Total Allocation)."); + + _cacheAllocators.Add(new CacheMemoryAllocator(CacheSize)); + + int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(ref codeSize, alignment); + if (allocOffsetNew < 0) + { + throw new OutOfMemoryException("Failed to allocate in new Cache Region!"); + } + + newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize); + return allocOffsetNew; } private static int AlignCodeSize(int codeSize, bool deferProtect = false) @@ -251,4 +299,4 @@ namespace ARMeilleure.Translation.Cache return false; } } -} \ No newline at end of file +} diff --git a/src/MeloNX/MeloNX/App/Views/Main/ContentView.swift b/src/MeloNX/MeloNX/App/Views/Main/ContentView.swift index 89af773c4..d03fcc3ae 100644 --- a/src/MeloNX/MeloNX/App/Views/Main/ContentView.swift +++ b/src/MeloNX/MeloNX/App/Views/Main/ContentView.swift @@ -302,8 +302,6 @@ struct ContentView: View { } private func setupEmulation() { - refreshControllersList() - isVCA = (currentControllers.first(where: { $0 == onscreencontroller }) != nil) DispatchQueue.main.async { @@ -321,34 +319,14 @@ struct ContentView: View { controllersList.removeAll(where: { $0.id == "0" || (!$0.name.starts(with: "GC - ") && $0 != onscreencontroller) }) controllersList.mutableForEach { $0.name = $0.name.replacingOccurrences(of: "GC - ", with: "") } + currentControllers = [] - if !currentControllers.isEmpty, !(currentControllers.count == 1) { - var currentController: [Controller] = [] - - if currentController.count == 1 { - currentController.append(controllersList[0]) - } else if (controllersList.count - 1) >= 1 { - for controller in controllersList { - if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) { - currentController.append(controller) - } - } - } - - if currentController == currentControllers { - currentControllers = [] - currentControllers = currentController - } - } else { - currentControllers = [] - - if controllersList.count == 1 { - currentControllers.append(controllersList[0]) - } else if (controllersList.count - 1) >= 1 { - for controller in controllersList { - if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) { - currentControllers.append(controller) - } + if controllersList.count == 1 { + currentControllers.append(controllersList[0]) + } else if (controllersList.count - 1) >= 1 { + for controller in controllersList { + if controller.id != onscreencontroller.id && !currentControllers.contains(where: { $0.id == controller.id }) { + currentControllers.append(controller) } } } @@ -412,6 +390,7 @@ struct ContentView: View { if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), components.host == "game" { DispatchQueue.main.async { + refreshControllersList() if let text = components.queryItems?.first(where: { $0.name == "id" })?.value { game = ryujinx.games.first(where: { $0.titleId == text }) } else if let text = components.queryItems?.first(where: { $0.name == "name" })?.value {