Set controller code back and add back Software JIT Cache Regions

This commit is contained in:
Stossy11 2025-04-11 22:37:46 +10:00
parent c5c79c26ea
commit 90859393a3
2 changed files with 96 additions and 69 deletions

View file

@ -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<CacheMemoryAllocator> _cacheAllocators = [];
private static readonly List<CacheEntry> _cacheEntries = new();
private static readonly object _lock = new();
private static bool _initialized;
private static readonly List<ReservedRegion> _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;
}
}
}
}

View file

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