PPTC Profiles & FunctionTable options

The Sparse Jit Function Table sizes now depend on the LowPowerPTC setting. This means lower power devices won't be impacted as hard by the higher ram speed requirement of GiantBlock.
Also added functionality to the PPTC Initializer so it now supports different PPTC Profiles simultaneously, which makes switching between TinyBlock/LowPower and GiantBlock/HighPower seamless.
This also opens the door for the potential of PPTC cache with exefs mods enabled in the future.
Default (aka HighPower) currently has an Avalonia bug that causes a crash when starting a game, it can be bypassed be clicking the window multiple times durring loading until the window unfreezes.
This commit is contained in:
LotP1 2024-11-16 19:02:46 +01:00
parent 3f6d42a449
commit b9c20e551a
23 changed files with 79 additions and 42 deletions

View file

@ -36,11 +36,32 @@ namespace ARMeilleure.Common
new( 1, 9),
};
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse)
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
new AddressTableLevel[]
{
new( 38, 1),
new( 2, 36),
};
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
new AddressTableLevel[]
{
new( 31, 1),
new( 1, 30),
};
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
{
if (sparse)
{
if (lowPower)
{
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
}
else
{
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
}
}
else
{

View file

@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 6986; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 6991; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@ -102,7 +102,7 @@ namespace ARMeilleure.Translation.PTC
Disable();
}
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
{
Wait();
@ -141,8 +141,8 @@ namespace ARMeilleure.Translation.PTC
Directory.CreateDirectory(workPathBackup);
}
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
PreLoad();
Profiler.PreLoad();

View file

@ -58,9 +58,9 @@ namespace ARMeilleure.Translation
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
}
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
{
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
return _ptc;
}

View file

@ -178,12 +178,12 @@ namespace ARMeilleure.Common
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
/// <param name="type">Memory manager type</param>
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type)
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type, bool lowPower)
{
// Assume software memory means that we don't want to use any signal handlers.
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable;
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse, lowPower), sparse);
}
/// <summary>

View file

@ -9,7 +9,7 @@ namespace Ryujinx.Cpu.AppleHv
private readonly ITickSource _tickSource;
private readonly HvMemoryManager _memoryManager;
public HvCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
public HvCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit, bool lowPower)
{
_tickSource = tickSource;
_memoryManager = (HvMemoryManager)memory;
@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv
{
}
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
{
return new DummyDiskCacheLoadState();
}

View file

@ -14,9 +14,9 @@ namespace Ryujinx.Cpu.AppleHv
}
/// <inheritdoc/>
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit, bool lowPower)
{
return new HvCpuContext(_tickSource, memoryManager, for64Bit);
return new HvCpuContext(_tickSource, memoryManager, for64Bit, lowPower);
}
}
}

View file

@ -48,7 +48,7 @@ namespace Ryujinx.Cpu
/// <param name="displayVersion">Version of the application</param>
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
/// <returns>Disk cache load progress reporter and manager</returns>
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled);
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector);
/// <summary>
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.

View file

@ -13,6 +13,6 @@ namespace Ryujinx.Cpu
/// <param name="memoryManager">Memory manager for the address space of the context</param>
/// <param name="for64Bit">Indicates if the context will be used to run 64-bit or 32-bit Arm code</param>
/// <returns>CPU context</returns>
ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit);
ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit, bool lowPower);
}
}

View file

@ -12,10 +12,10 @@ namespace Ryujinx.Cpu.Jit
private readonly Translator _translator;
private readonly AddressTable<ulong> _functionTable;
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit, bool lowPower)
{
_tickSource = tickSource;
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type, lowPower);
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
if (memory.Type.IsHostMappedOrTracked())
@ -50,9 +50,9 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
{
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled));
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector));
}
/// <inheritdoc/>

View file

@ -12,9 +12,9 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit, bool lowPower)
{
return new JitCpuContext(_tickSource, memoryManager, for64Bit);
return new JitCpuContext(_tickSource, memoryManager, for64Bit, lowPower);
}
}
}

View file

@ -11,11 +11,11 @@ namespace Ryujinx.Cpu.LightningJit
private readonly Translator _translator;
private readonly AddressTable<ulong> _functionTable;
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit, bool lowPower)
{
_tickSource = tickSource;
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type, lowPower);
_translator = new Translator(memory, _functionTable);
@ -46,7 +46,7 @@ namespace Ryujinx.Cpu.LightningJit
}
/// <inheritdoc/>
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
{
return new DummyDiskCacheLoadState();
}

View file

@ -12,9 +12,9 @@ namespace Ryujinx.Cpu.LightningJit
}
/// <inheritdoc/>
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit)
public ICpuContext CreateCpuContext(IMemoryManager memoryManager, bool for64Bit, bool lowPower)
{
return new LightningJitCpuContext(_tickSource, memoryManager, for64Bit);
return new LightningJitCpuContext(_tickSource, memoryManager, for64Bit, lowPower);
}
}
}

View file

@ -98,6 +98,11 @@ namespace Ryujinx.HLE
/// </summary>
internal readonly bool EnablePtc;
/// <summary>
/// Control if the Profiled Translation Cache (PTC) should run in low power mode.
/// </summary>
internal readonly bool LowPowerPtc;
/// <summary>
/// Control if the guest application should be told that there is a Internet connection available.
/// </summary>
@ -198,6 +203,7 @@ namespace Ryujinx.HLE
bool enableVsync,
bool enableDockedMode,
bool enablePtc,
bool lowPowerPtc,
bool enableInternetAccess,
IntegrityCheckLevel fsIntegrityCheckLevel,
int fsGlobalAccessLogMode,
@ -228,6 +234,7 @@ namespace Ryujinx.HLE
EnableVsync = enableVsync;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
LowPowerPtc = lowPowerPtc;
EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode;

View file

@ -13,7 +13,8 @@ namespace Ryujinx.HLE.HOS
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize);
ulong codeSize,
string cacheSelector);
}
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
@ -33,7 +34,8 @@ namespace Ryujinx.HLE.HOS
GpuContext gpuContext,
T memoryManager,
ulong addressSpaceSize,
bool for64Bit)
bool for64Bit,
bool lowPower)
{
if (memoryManager is IRefCounted rc)
{
@ -44,7 +46,7 @@ namespace Ryujinx.HLE.HOS
_pid = pid;
_gpuContext = gpuContext;
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit);
_cpuContext = cpuEngine.CreateCpuContext(memoryManager, for64Bit, lowPower);
_memoryManager = memoryManager;
AddressSpaceSize = addressSpaceSize;
@ -67,10 +69,11 @@ namespace Ryujinx.HLE.HOS
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize)
ulong codeSize,
string cacheSelector)
{
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled);
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled, cacheSelector);
}
public void InvalidateCacheRegion(ulong address, ulong size)

View file

@ -48,12 +48,13 @@ namespace Ryujinx.HLE.HOS
IArmProcessContext processContext;
bool isArm64Host = RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
bool isLowPower = context.Device.Configuration.LowPowerPtc;
if (OperatingSystem.IsMacOS() && isArm64Host && for64Bit && context.Device.Configuration.UseHypervisor)
{
var cpuEngine = new HvEngine(_tickSource);
var memoryManager = new HvMemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
processContext = new ArmProcessContext<HvMemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit, isLowPower);
}
else
{
@ -87,7 +88,7 @@ namespace Ryujinx.HLE.HOS
{
case MemoryManagerMode.SoftwarePageTable:
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit);
processContext = new ArmProcessContext<MemoryManager>(pid, cpuEngine, _gpu, memoryManager, addressSpaceSize, for64Bit, isLowPower);
break;
case MemoryManagerMode.HostMapped:
@ -95,7 +96,7 @@ namespace Ryujinx.HLE.HOS
if (addressSpace == null)
{
var memoryManagerHostTracked = new MemoryManagerHostTracked(context.Memory, addressSpaceSize, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
processContext = new ArmProcessContext<MemoryManagerHostTracked>(pid, cpuEngine, _gpu, memoryManagerHostTracked, addressSpaceSize, for64Bit);
processContext = new ArmProcessContext<MemoryManagerHostTracked>(pid, cpuEngine, _gpu, memoryManagerHostTracked, addressSpaceSize, for64Bit, isLowPower);
}
else
{
@ -105,7 +106,7 @@ namespace Ryujinx.HLE.HOS
}
var memoryManagerHostMapped = new MemoryManagerHostMapped(addressSpace, mode == MemoryManagerMode.HostMappedUnsafe, invalidAccessHandler);
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, memoryManagerHostMapped, addressSpace.AddressSpaceSize, for64Bit);
processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, cpuEngine, _gpu, memoryManagerHostMapped, addressSpace.AddressSpaceSize, for64Bit, isLowPower);
}
break;
@ -114,7 +115,7 @@ namespace Ryujinx.HLE.HOS
}
}
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, isLowPower ? "LowPower" : "HighPower");
return processContext;
}

View file

@ -106,6 +106,9 @@ namespace Ryujinx.Headless.SDL2
[Option("disable-ptc", Required = false, HelpText = "Disables profiled persistent translation cache.")]
public bool DisablePTC { get; set; }
[Option("low-power-ptc", Required = false, HelpText = "Increases PTC performance for low power systems.")]
public bool LowPowerPTC { get; set; }
[Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")]
public bool EnableInternetAccess { get; set; }

View file

@ -566,6 +566,7 @@ namespace Ryujinx.Headless.SDL2
!options.DisableVSync,
!options.DisableDockedMode,
!options.DisablePTC,
options.LowPowerPTC,
options.EnableInternetAccess,
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode,

View file

@ -11,9 +11,9 @@ namespace Ryujinx.Tests.Cpu
{
private readonly Translator _translator;
public CpuContext(IMemoryManager memory, bool for64Bit)
public CpuContext(IMemoryManager memory, bool for64Bit, bool lowPower)
{
_translator = new Translator(new JitMemoryAllocator(), memory, AddressTable<ulong>.CreateForArm(for64Bit, memory.Type));
_translator = new Translator(new JitMemoryAllocator(), memory, AddressTable<ulong>.CreateForArm(for64Bit, memory.Type, lowPower));
memory.UnmapEvent += UnmapHandler;
}

View file

@ -62,7 +62,7 @@ namespace Ryujinx.Tests.Cpu
_context = CpuContext.CreateExecutionContext();
_cpuContext = new CpuContext(_memory, for64Bit: true);
_cpuContext = new CpuContext(_memory, for64Bit: true, lowPower: false);
// Prevent registering LCQ functions in the FunctionTable to avoid initializing and populating the table,
// which improves test durations.

View file

@ -57,7 +57,7 @@ namespace Ryujinx.Tests.Cpu
_context = CpuContext.CreateExecutionContext();
_context.IsAarch32 = true;
_cpuContext = new CpuContext(_memory, for64Bit: false);
_cpuContext = new CpuContext(_memory, for64Bit: false, lowPower: false);
// Prevent registering LCQ functions in the FunctionTable to avoid initializing and populating the table,
// which improves test durations.

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Tests.Cpu
_translator ??= new Translator(
new JitMemoryAllocator(),
new MockMemoryManager(),
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable));
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable, lowPower: false));
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]

View file

@ -58,7 +58,7 @@ namespace Ryujinx.Tests.Memory
_translator ??= new Translator(
new JitMemoryAllocator(),
new MockMemoryManager(),
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable));
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable, lowPower: false));
}
[Test]

View file

@ -871,6 +871,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.EnableVsync,
ConfigurationState.Instance.System.EnableDockedMode,
ConfigurationState.Instance.System.EnablePtc,
ConfigurationState.Instance.System.EnableLowPowerPtc,
ConfigurationState.Instance.System.EnableInternetAccess,
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
ConfigurationState.Instance.System.FsGlobalAccessLogMode,