diff --git a/src/ARMeilleure/Memory/ReservedRegion.cs b/src/ARMeilleure/Memory/ReservedRegion.cs index 3870d4c84..52542cd22 100644 --- a/src/ARMeilleure/Memory/ReservedRegion.cs +++ b/src/ARMeilleure/Memory/ReservedRegion.cs @@ -7,6 +7,7 @@ namespace ARMeilleure.Memory public const int DefaultGranularity = 65536; // Mapping granularity in Windows. public IJitMemoryBlock Block { get; } + public IJitMemoryAllocator Allocator { get; } public IntPtr Pointer => Block.Pointer; @@ -21,6 +22,7 @@ namespace ARMeilleure.Memory granularity = DefaultGranularity; } + Allocator = allocator; Block = allocator.Reserve(maxSize); _maxSize = maxSize; _sizeGranularity = granularity; diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index ffcb3540f..0da38b918 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; @@ -18,7 +19,7 @@ namespace ARMeilleure.Translation.Cache private static readonly int _pageMask = _pageSize - 4; private const int CodeAlignment = 4; // Bytes. - private const int CacheSize = 1024 * 1024 * 1024; + private const int CacheSize = 128 * 1024 * 1024; private const int CacheSizeIOS = 128 * 1024 * 1024; private static ReservedRegion _jitRegion; @@ -31,6 +32,10 @@ namespace ARMeilleure.Translation.Cache 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); @@ -49,7 +54,11 @@ namespace ARMeilleure.Translation.Cache return; } - _jitRegion = new ReservedRegion(allocator, (ulong)(OperatingSystem.IsIOS() ? CacheSizeIOS : CacheSize)); + var firstRegion = new ReservedRegion(allocator, CacheSize); + + + _jitRegions.Add(firstRegion); + _activeRegionIndex = 0; if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS() && !OperatingSystem.IsIOS()) { @@ -60,7 +69,9 @@ namespace ARMeilleure.Translation.Cache 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 +84,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,21 +100,14 @@ 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()) { Marshal.Copy(code, 0, funcPtr, code.Length); - if (deferProtect) - { - _deferredRxProtect.Enqueue((funcOffset, code.Length)); - } - else - { - ReprotectAsExecutable(funcOffset, code.Length); - - JitSupportDarwinAot.Invalidate(funcPtr, (ulong)code.Length); - } + ReprotectAsExecutable(targetRegion, funcOffset, code.Length); + JitSupportDarwinAot.Invalidate(funcPtr, (ulong)code.Length); } else if (OperatingSystem.IsMacOS()&& RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { @@ -115,9 +121,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 +145,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) + { + _cacheAllocator.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,20 +202,35 @@ namespace ARMeilleure.Translation.Cache alignment = 0x4000; } - int allocOffset = _cacheAllocator.Allocate(ref codeSize, alignment); - - //DEBUG: Show JIT Memory Allocation - - //Console.WriteLine($"{allocOffset:x8}: {codeSize:x8} {alignment:x8}"); - - if (allocOffset < 0) + for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) { - throw new OutOfMemoryException("JIT Cache exhausted."); + int allocOffset = _cacheAllocator.Allocate(ref codeSize, alignment); + + if (allocOffset >= 0) + { + _jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + _activeRegionIndex = i; + 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; + + _cacheAllocator = new CacheMemoryAllocator(CacheSize); + + int allocOffsetNew = _cacheAllocator.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) diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index f6b1e361d..65a102bc9 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist b/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist index 19b8e18b3..62375ba69 100644 --- a/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/src/MeloNX/MeloNX.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,12 +12,12 @@ Ryujinx.xcscheme_^#shared#^_ orderHint - 4 + 1 com.Stossy11.MeloNX.RyujinxAg.xcscheme_^#shared#^_ orderHint - 3 + 2 SuppressBuildableAutocreation diff --git a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift index f41d2bdb0..a4a2558ee 100644 --- a/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift +++ b/src/MeloNX/MeloNX/App/Core/Ryujinx/Ryujinx.swift @@ -77,6 +77,7 @@ class Ryujinx { var disablevsync: Bool var language: SystemLanguage var regioncode: SystemRegionCode + var handHeldController: Bool init(gamepath: String, @@ -102,7 +103,8 @@ class Ryujinx { disablePTC: Bool = false, disablevsync: Bool = false, language: SystemLanguage = .americanEnglish, - regioncode: SystemRegionCode = .usa + regioncode: SystemRegionCode = .usa, + handHeldController: Bool = false ) { self.gamepath = gamepath self.inputids = inputids @@ -128,6 +130,7 @@ class Ryujinx { self.disablevsync = disablevsync self.language = language self.regioncode = regioncode + self.handHeldController = handHeldController } } @@ -321,10 +324,14 @@ class Ryujinx { args.append("--list-inputs-ids") } - // Append the input ids (limit to 4 just in case) + // Append the input ids (limit to 8 (used to be 4) just in case) if !config.inputids.isEmpty { - config.inputids.prefix(4).enumerated().forEach { index, inputId in - args.append(contentsOf: ["--input-id-\(index + 1)", inputId]) + config.inputids.prefix(8).enumerated().forEach { index, inputId in + if config.handHeldController { + args.append(contentsOf: ["\(index == 0 ? "--input-id-handheld" : "--input-id-\(index + 1)")", inputId]) + } else { + args.append(contentsOf: ["--input-id-\(index + 1)", inputId]) + } } } diff --git a/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift b/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift index cca4f36e1..a7fa63405 100644 --- a/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift +++ b/src/MeloNX/MeloNX/App/Views/SettingsView/SettingsView.swift @@ -272,42 +272,12 @@ struct SettingsView: View { Text("Select input devices and on-screen controls to play with. ") } - // Language and Region Settings - Section { - Picker(selection: $config.language) { - ForEach(SystemLanguage.allCases, id: \.self) { ratio in - Text(ratio.displayName).tag(ratio) - } - } label: { - labelWithIcon("Language", iconName: "character.bubble") - } - - Picker(selection: $config.regioncode) { - ForEach(SystemRegionCode.allCases, id: \.self) { ratio in - Text(ratio.displayName).tag(ratio) - } - } label: { - labelWithIcon("Region", iconName: "globe") - } - - - // globe - } header: { - Text("Language and Region Settings") - .font(.title3.weight(.semibold)) - .textCase(nil) - .headerProminence(.increased) - } footer: { - Text("Configure the System Language and the Region.") - } - // Input Settings Section { - - Toggle(isOn: $config.listinputids) { - labelWithIcon("List Input IDs", iconName: "list.bullet") - } - .tint(.blue) + Toggle(isOn: $config.macroHLE) { + labelWithIcon("Player 1 to Handheld Input", iconName: "formfitting.gamecontroller") + }.tint(.blue) + Toggle(isOn: $ryuDemo) { labelWithIcon("On-Screen Controller (Demo)", iconName: "hand.draw") @@ -363,6 +333,35 @@ struct SettingsView: View { Text("Configure input devices and on-screen controls for easier navigation and play.") } + // Language and Region Settings + Section { + Picker(selection: $config.language) { + ForEach(SystemLanguage.allCases, id: \.self) { ratio in + Text(ratio.displayName).tag(ratio) + } + } label: { + labelWithIcon("Language", iconName: "character.bubble") + } + + Picker(selection: $config.regioncode) { + ForEach(SystemRegionCode.allCases, id: \.self) { ratio in + Text(ratio.displayName).tag(ratio) + } + } label: { + labelWithIcon("Region", iconName: "globe") + } + + + // globe + } header: { + Text("Language and Region Settings") + .font(.title3.weight(.semibold)) + .textCase(nil) + .headerProminence(.increased) + } footer: { + Text("Configure the System Language and the Region.") + } + // CPU Mode Section { if filteredMemoryModes.isEmpty {