mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-06-26 22:29:04 +00:00
Compare commits
5 Commits
Canary-1.3
...
Canary-1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b1b015572 | ||
|
|
c2e0cf2fd5 | ||
|
|
6eb6b1fc81 | ||
|
|
0ce1bf0b33 | ||
|
|
c698a10b23 |
@@ -39,13 +39,12 @@
|
||||
<!-- OpenTk.Audio.OpenAL has moved to OpenTk.Audio -->
|
||||
<!--<PackageVersion Include="OpenTK.Audio" Version="5.0.0-pre.15" />-->
|
||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
|
||||
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
||||
<!-- Ryujinx.Audio.OpenAL.Dependencies is from the original project, last updated 12/30/20 -->
|
||||
<!--<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />-->
|
||||
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.2" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.4-build6" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.3" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.2-ryujinx.4" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
|
||||
<PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" />
|
||||
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu.AppleHv.Arm;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
@@ -17,9 +18,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
|
||||
if (currentEl == (uint)ExceptionLevel.EL1h)
|
||||
{
|
||||
return _impl.ElrEl1;
|
||||
}
|
||||
return _impl.Pc;
|
||||
}
|
||||
}
|
||||
@@ -69,9 +68,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +79,13 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
private readonly IHvExecutionContext _shadowContext;
|
||||
private IHvExecutionContext _impl;
|
||||
private int _shouldStep;
|
||||
|
||||
private readonly ExceptionCallbacks _exceptionCallbacks;
|
||||
|
||||
private int _interruptRequested;
|
||||
|
||||
// GPU Sync control
|
||||
private int _syncCounter;
|
||||
private int _strongSyncCounter;
|
||||
|
||||
public HvExecutionContext(ICounter counter, ExceptionCallbacks exceptionCallbacks)
|
||||
{
|
||||
_counter = counter;
|
||||
@@ -108,38 +107,17 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
/// <inheritdoc/>
|
||||
public void SetV(int index, V128 value) => _impl.SetV(index, value);
|
||||
|
||||
private void InterruptHandler()
|
||||
{
|
||||
_exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||
}
|
||||
|
||||
private void BreakHandler(ulong address, int imm)
|
||||
{
|
||||
_exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
|
||||
}
|
||||
|
||||
private void StepHandler()
|
||||
{
|
||||
_exceptionCallbacks.StepCallback?.Invoke(this);
|
||||
}
|
||||
|
||||
private void SupervisorCallHandler(ulong address, int imm)
|
||||
{
|
||||
_exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
|
||||
}
|
||||
|
||||
private void UndefinedHandler(ulong address, int opCode)
|
||||
{
|
||||
_exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
|
||||
}
|
||||
private void InterruptHandler() => _exceptionCallbacks.InterruptCallback?.Invoke(this);
|
||||
private void BreakHandler(ulong address, int imm) => _exceptionCallbacks.BreakCallback?.Invoke(this, address, imm);
|
||||
private void StepHandler() => _exceptionCallbacks.StepCallback?.Invoke(this);
|
||||
private void SupervisorCallHandler(ulong address, int imm) => _exceptionCallbacks.SupervisorCallback?.Invoke(this, address, imm);
|
||||
private void UndefinedHandler(ulong address, int opCode) => _exceptionCallbacks.UndefinedCallback?.Invoke(this, address, opCode);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0 && _impl is HvExecutionContextVcpu impl)
|
||||
{
|
||||
impl.RequestInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetAndClearInterruptRequested()
|
||||
@@ -161,13 +139,9 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
uint currentEl = Pstate & ~((uint)ExceptionLevel.PstateMask);
|
||||
if (currentEl == (uint)ExceptionLevel.EL1h)
|
||||
{
|
||||
_impl.ElrEl1 = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_impl.Pc = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,9 +155,11 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
public unsafe void Execute(HvMemoryManager memoryManager, ulong address)
|
||||
{
|
||||
HvVcpu vcpu = HvVcpuPool.Instance.Create(memoryManager.AddressSpace, _shadowContext, SwapContext);
|
||||
|
||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||
|
||||
_syncCounter = 0;
|
||||
_strongSyncCounter = 0;
|
||||
|
||||
while (Running)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _shouldStep, 0, 1) == 1)
|
||||
@@ -192,16 +168,23 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
if (currentEl == (uint)ExceptionLevel.EL1h)
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
|
||||
spsr |= (1 << 21);
|
||||
spsr |= (1U << 21);
|
||||
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.SPSR_EL1, spsr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pstate |= (1 << 21);
|
||||
Pstate |= (1U << 21);
|
||||
}
|
||||
HvApi.hv_vcpu_set_sys_reg(vcpu.Handle, HvSysReg.MDSCR_EL1, 1);
|
||||
}
|
||||
|
||||
// Adaptive GPU synchronization to prevent 0 FPS
|
||||
if (++_syncCounter % 12 == 0)
|
||||
{
|
||||
TryGpuSync();
|
||||
_syncCounter = 0;
|
||||
}
|
||||
|
||||
HvApi.hv_vcpu_run(vcpu.Handle).ThrowOnError();
|
||||
|
||||
HvExitReason reason = vcpu.ExitInfo->Reason;
|
||||
@@ -212,9 +195,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
ExceptionClass hvEc = (ExceptionClass)(hvEsr >> 26);
|
||||
|
||||
if (hvEc != ExceptionClass.HvcAarch64)
|
||||
{
|
||||
throw new Exception($"Unhandled exception from guest kernel with ESR 0x{hvEsr:X} ({hvEc}).");
|
||||
}
|
||||
|
||||
address = SynchronousException(memoryManager, ref vcpu);
|
||||
HvApi.hv_vcpu_set_reg(vcpu.Handle, HvReg.PC, address).ThrowOnError();
|
||||
@@ -245,10 +226,31 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
HvVcpuPool.Instance.Destroy(vcpu, SwapContext);
|
||||
}
|
||||
|
||||
// TryGpuSync() is called periodically in the main Execute() loop. The "syncing" value can be tuned based on gameplay results.
|
||||
// This feature it to be followed-up and further completed in a future PR.
|
||||
private void TryGpuSync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.Yield();
|
||||
|
||||
if (++_strongSyncCounter % 6 == 0)
|
||||
{
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_strongSyncCounter % 100 == 0)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Gpu, $"[AppleHv] GPU sync issue: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ulong SynchronousException(HvMemoryManager memoryManager, ref HvVcpu vcpu)
|
||||
{
|
||||
ulong vcpuHandle = vcpu.Handle;
|
||||
|
||||
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
|
||||
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
|
||||
|
||||
@@ -259,16 +261,20 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
case ExceptionClass.DataAbortLowerEl:
|
||||
DataAbort(memoryManager.Tracking, vcpuHandle, (uint)esr);
|
||||
break;
|
||||
|
||||
case ExceptionClass.TrappedMsrMrsSystem:
|
||||
InstructionTrap((uint)esr);
|
||||
HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.ELR_EL1, elr + 4UL).ThrowOnError();
|
||||
break;
|
||||
|
||||
case ExceptionClass.SvcAarch64:
|
||||
ReturnToPool(vcpu);
|
||||
ushort id = (ushort)esr;
|
||||
SupervisorCallHandler(elr - 4UL, id);
|
||||
Thread.Yield(); // MoltenVK causes extremely frequent SVC exits, and HVF handles them in a busy loop. Hypervisor.Framework accelerates the guest CPU, and without periodic yielding/flushing, MoltenVK's presentation queue can starve, causing permanent 0 FPS deadlock.
|
||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||
break;
|
||||
|
||||
case ExceptionClass.SoftwareStepLowerEl:
|
||||
HvApi.hv_vcpu_get_sys_reg(vcpuHandle, HvSysReg.SPSR_EL1, out ulong spsr).ThrowOnError();
|
||||
spsr &= ~((ulong)(1 << 21));
|
||||
@@ -278,21 +284,23 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
StepHandler();
|
||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||
break;
|
||||
|
||||
case ExceptionClass.BrkAarch64:
|
||||
ReturnToPool(vcpu);
|
||||
BreakHandler(elr, (ushort)esr);
|
||||
vcpu = RentFromPool(memoryManager.AddressSpace, vcpu);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception($"Unhandled guest exception {ec}.");
|
||||
}
|
||||
|
||||
// Make sure we will continue running at EL0.
|
||||
if (memoryManager.AddressSpace.GetAndClearUserTlbInvalidationPending())
|
||||
{
|
||||
|
||||
// TODO: Invalidate only the range that was modified?
|
||||
return HvAddressSpace.KernelRegionTlbiEretAddress;
|
||||
}
|
||||
|
||||
return HvAddressSpace.KernelRegionEretAddress;
|
||||
}
|
||||
|
||||
@@ -305,7 +313,6 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
if (farValid)
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(vcpu, HvSysReg.FAR_EL1, out ulong far).ThrowOnError();
|
||||
|
||||
ulong size = 1UL << accessSizeLog2;
|
||||
|
||||
if (!tracking.VirtualMemoryEvent(far, size, write))
|
||||
@@ -349,9 +356,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
private void WriteRt(uint rt, ulong value)
|
||||
{
|
||||
if (rt < 31)
|
||||
{
|
||||
SetX((int)rt, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReturnToPool(HvVcpu vcpu)
|
||||
@@ -369,8 +374,6 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
_impl = newContext;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using ARMeilleure.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
@@ -14,8 +16,31 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
private static readonly SetSimdFpReg _setSimdFpReg;
|
||||
private static readonly nint _setSimdFpRegNativePtr;
|
||||
|
||||
public static bool AggressiveMode { get; set; } = false;
|
||||
private bool _earlyBootPhase = true;
|
||||
|
||||
public ulong ThreadUid { get; set; }
|
||||
|
||||
private readonly ulong[] _x = new ulong[32];
|
||||
private readonly V128[] _v = new V128[32];
|
||||
|
||||
private ulong _pc;
|
||||
private ulong _elrEl1;
|
||||
private ulong _esrEl1;
|
||||
private ulong _tpidrEl0;
|
||||
private ulong _tpidrroEl0;
|
||||
private ulong _fpcr;
|
||||
private ulong _fpsr;
|
||||
private ulong _pstateRaw;
|
||||
|
||||
private long _fallbackCount;
|
||||
private long _lastWarningTicks;
|
||||
private const long WarningCooldownTicks = 500_000_000; // 0.5 seconds
|
||||
|
||||
private readonly ulong _vcpu;
|
||||
private int _interruptRequested;
|
||||
private readonly object _registerLock = new object();
|
||||
|
||||
static HvExecutionContextVcpu()
|
||||
{
|
||||
// .NET does not support passing vectors by value, so we need to pass a pointer and use a native
|
||||
@@ -33,155 +58,293 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
}
|
||||
}
|
||||
|
||||
public HvExecutionContextVcpu(ulong vcpu)
|
||||
{
|
||||
_vcpu = vcpu;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
lock (_registerLock)
|
||||
{
|
||||
_pstateRaw = 0x80000000UL;
|
||||
_pc = 0;
|
||||
_elrEl1 = 0;
|
||||
_esrEl1 = 0;
|
||||
_tpidrEl0 = 0;
|
||||
_tpidrroEl0 = 0;
|
||||
_fpcr = 0;
|
||||
_fpsr = 0;
|
||||
|
||||
Array.Clear(_x, 0, _x.Length);
|
||||
Array.Clear(_v, 0, _v.Length);
|
||||
|
||||
_fallbackCount = 0;
|
||||
_lastWarningTicks = 0;
|
||||
_interruptRequested = 0;
|
||||
_earlyBootPhase = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void LogHvWarning(string operation, string regName, string extra = "")
|
||||
{
|
||||
if (AggressiveMode) return;
|
||||
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
if (now - _lastWarningTicks <= WarningCooldownTicks) return;
|
||||
|
||||
string msg = $"[AppleHv] BadArgument on {operation} {regName} | PC=0x{_pc:X16}";
|
||||
if (!string.IsNullOrEmpty(extra)) msg += $" | {extra}";
|
||||
msg += $" | Total: {Interlocked.Read(ref _fallbackCount)}";
|
||||
|
||||
Logger.Warning?.Print(LogClass.Cpu, msg);
|
||||
_lastWarningTicks = now;
|
||||
}
|
||||
|
||||
public ulong Pc
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.PC, out ulong pc).ThrowOnError();
|
||||
return pc;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.PC, value).ThrowOnError();
|
||||
}
|
||||
get { lock (_registerLock) return GetRegCached(HvReg.PC, ref _pc, "PC"); }
|
||||
set { lock (_registerLock) SetRegCached(HvReg.PC, value, ref _pc, "PC"); }
|
||||
}
|
||||
|
||||
public ulong ElrEl1
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.ELR_EL1, out ulong elr).ThrowOnError();
|
||||
return elr;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.ELR_EL1, value).ThrowOnError();
|
||||
}
|
||||
get { lock (_registerLock) return GetSysRegCached(HvSysReg.ELR_EL1, ref _elrEl1, "ELR_EL1"); }
|
||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.ELR_EL1, value, ref _elrEl1, "ELR_EL1"); }
|
||||
}
|
||||
|
||||
public ulong EsrEl1
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.ESR_EL1, out ulong esr).ThrowOnError();
|
||||
return esr;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.ESR_EL1, value).ThrowOnError();
|
||||
}
|
||||
get { lock (_registerLock) return GetSysRegCached(HvSysReg.ESR_EL1, ref _esrEl1, "ESR_EL1"); }
|
||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.ESR_EL1, value, ref _esrEl1, "ESR_EL1"); }
|
||||
}
|
||||
|
||||
public long TpidrEl0
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.TPIDR_EL0, out ulong tpidrEl0).ThrowOnError();
|
||||
return (long)tpidrEl0;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.TPIDR_EL0, (ulong)value).ThrowOnError();
|
||||
}
|
||||
get { lock (_registerLock) return (long)GetSysRegCached(HvSysReg.TPIDR_EL0, ref _tpidrEl0, "TPIDR_EL0"); }
|
||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.TPIDR_EL0, (ulong)value, ref _tpidrEl0, "TPIDR_EL0"); }
|
||||
}
|
||||
|
||||
public long TpidrroEl0
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.TPIDRRO_EL0, out ulong tpidrroEl0).ThrowOnError();
|
||||
return (long)tpidrroEl0;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.TPIDRRO_EL0, (ulong)value).ThrowOnError();
|
||||
}
|
||||
get { lock (_registerLock) return (long)GetSysRegCached(HvSysReg.TPIDRRO_EL0, ref _tpidrroEl0, "TPIDRRO_EL0"); }
|
||||
set { lock (_registerLock) SetSysRegCached(HvSysReg.TPIDRRO_EL0, (ulong)value, ref _tpidrroEl0, "TPIDRRO_EL0"); }
|
||||
}
|
||||
|
||||
public uint Pstate
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.CPSR, out ulong cpsr).ThrowOnError();
|
||||
return (uint)cpsr;
|
||||
lock (_registerLock)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_get_reg(_vcpu, HvReg.CPSR, out ulong val);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("Get", "CPSR (Pstate)");
|
||||
return (uint)_pstateRaw;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
_pstateRaw = val;
|
||||
return (uint)val;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.CPSR, (ulong)value).ThrowOnError();
|
||||
lock (_registerLock)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_set_reg(_vcpu, HvReg.CPSR, value);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("Set", "CPSR (Pstate)", $"value=0x{value:X}");
|
||||
}
|
||||
else res.ThrowOnError();
|
||||
_pstateRaw = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint Fpcr
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.FPCR, out ulong fpcr).ThrowOnError();
|
||||
return (uint)fpcr;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.FPCR, (ulong)value).ThrowOnError();
|
||||
}
|
||||
get { lock (_registerLock) return (uint)GetRegCached(HvReg.FPCR, ref _fpcr, "FPCR"); }
|
||||
set { lock (_registerLock) SetRegCached(HvReg.FPCR, value, ref _fpcr, "FPCR"); }
|
||||
}
|
||||
|
||||
public uint Fpsr
|
||||
{
|
||||
get
|
||||
{
|
||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.FPSR, out ulong fpsr).ThrowOnError();
|
||||
return (uint)fpsr;
|
||||
}
|
||||
set
|
||||
{
|
||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.FPSR, (ulong)value).ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ulong _vcpu;
|
||||
private int _interruptRequested;
|
||||
|
||||
public HvExecutionContextVcpu(ulong vcpu)
|
||||
{
|
||||
_vcpu = vcpu;
|
||||
get { lock (_registerLock) return (uint)GetRegCached(HvReg.FPSR, ref _fpsr, "FPSR"); }
|
||||
set { lock (_registerLock) SetRegCached(HvReg.FPSR, value, ref _fpsr, "FPSR"); }
|
||||
}
|
||||
|
||||
public ulong GetX(int index)
|
||||
{
|
||||
if (index == 31)
|
||||
lock (_registerLock)
|
||||
{
|
||||
HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out ulong value).ThrowOnError();
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
HvApi.hv_vcpu_get_reg(_vcpu, HvReg.X0 + (uint)index, out ulong value).ThrowOnError();
|
||||
return value;
|
||||
ulong value;
|
||||
string regName = index == 31 ? "SP_EL0" : $"X{index}";
|
||||
|
||||
if (index == 31)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_get_sys_reg(_vcpu, HvSysReg.SP_EL0, out value);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("GetX", regName);
|
||||
return _x[31];
|
||||
}
|
||||
res.ThrowOnError();
|
||||
return _x[31] = value;
|
||||
}
|
||||
|
||||
if ((uint)index > 30) return 0;
|
||||
|
||||
if (index == 0 && _earlyBootPhase && _pc == 0)
|
||||
{
|
||||
return _x[0];
|
||||
}
|
||||
|
||||
HvResult resX = HvApi.hv_vcpu_get_reg(_vcpu, HvReg.X0 + (uint)index, out value);
|
||||
if (resX == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("GetX", regName);
|
||||
return _x[index];
|
||||
}
|
||||
resX.ThrowOnError();
|
||||
return _x[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetX(int index, ulong value)
|
||||
{
|
||||
if (index == 31)
|
||||
lock (_registerLock)
|
||||
{
|
||||
HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value).ThrowOnError();
|
||||
}
|
||||
else
|
||||
{
|
||||
HvApi.hv_vcpu_set_reg(_vcpu, HvReg.X0 + (uint)index, value).ThrowOnError();
|
||||
string regName = index == 31 ? "SP_EL0" : $"X{index}";
|
||||
|
||||
if (index == 31)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_set_sys_reg(_vcpu, HvSysReg.SP_EL0, value);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("SetX", regName, $"value=0x{value:X16}");
|
||||
_x[31] = value;
|
||||
return;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
_x[31] = value;
|
||||
}
|
||||
else if ((uint)index <= 30)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_set_reg(_vcpu, HvReg.X0 + (uint)index, value);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("SetX", regName, $"value=0x{value:X16}");
|
||||
_x[index] = value;
|
||||
return;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
_x[index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public V128 GetV(int index)
|
||||
{
|
||||
HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, HvSimdFPReg.Q0 + (uint)index, out HvSimdFPUchar16 value).ThrowOnError();
|
||||
return new V128(value.Low, value.High);
|
||||
lock (_registerLock)
|
||||
{
|
||||
if ((uint)index > 31) return default;
|
||||
|
||||
HvResult res = HvApi.hv_vcpu_get_simd_fp_reg(_vcpu, HvSimdFPReg.Q0 + (uint)index, out HvSimdFPUchar16 val);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("GetV", $"Q{index}");
|
||||
return _v[index];
|
||||
}
|
||||
res.ThrowOnError();
|
||||
return _v[index] = new V128(val.Low, val.High);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetV(int index, V128 value)
|
||||
{
|
||||
_setSimdFpReg(_vcpu, HvSimdFPReg.Q0 + (uint)index, value, _setSimdFpRegNativePtr).ThrowOnError();
|
||||
lock (_registerLock)
|
||||
{
|
||||
if ((uint)index > 31) return;
|
||||
|
||||
HvResult res = _setSimdFpReg(_vcpu, HvSimdFPReg.Q0 + (uint)index, value, _setSimdFpRegNativePtr);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("SetV", $"Q{index}");
|
||||
_v[index] = value;
|
||||
return;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
_v[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private ulong GetRegCached(HvReg reg, ref ulong cached, string name)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_get_reg(_vcpu, reg, out ulong val);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("GetReg", name);
|
||||
return cached;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
return cached = val;
|
||||
}
|
||||
|
||||
private void SetRegCached(HvReg reg, ulong value, ref ulong cached, string name)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_set_reg(_vcpu, reg, value);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("SetReg", name, $"value=0x{value:X16}");
|
||||
cached = value;
|
||||
return;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
cached = value;
|
||||
}
|
||||
|
||||
private ulong GetSysRegCached(HvSysReg reg, ref ulong cached, string name)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_get_sys_reg(_vcpu, reg, out ulong val);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("GetSysReg", name);
|
||||
return cached;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
return cached = val;
|
||||
}
|
||||
|
||||
private void SetSysRegCached(HvSysReg reg, ulong value, ref ulong cached, string name)
|
||||
{
|
||||
HvResult res = HvApi.hv_vcpu_set_sys_reg(_vcpu, reg, value);
|
||||
if (res == HvResult.BadArgument)
|
||||
{
|
||||
Interlocked.Increment(ref _fallbackCount);
|
||||
LogHvWarning("SetSysReg", name, $"value=0x{value:X16}");
|
||||
cached = value;
|
||||
return;
|
||||
}
|
||||
res.ThrowOnError();
|
||||
cached = value;
|
||||
}
|
||||
|
||||
public long GetFallbackCount() => Interlocked.Read(ref _fallbackCount);
|
||||
|
||||
public void RequestInterrupt()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _interruptRequested, 1) == 0)
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" />
|
||||
<PackageReference Include="Silk.NET.Shaderc" ExcludeAssets="native" />
|
||||
<PackageReference Include="Silk.NET.Vulkan" />
|
||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
||||
|
||||
@@ -689,13 +689,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
return context.Pstate & 0xFF0FFE20;
|
||||
}
|
||||
|
||||
private const long ContextCacheDurationTicks = 800_000; // ~0.8ms cache lifetime
|
||||
|
||||
private ThreadContext _cachedContext;
|
||||
private long _contextCacheTimestamp;
|
||||
private bool _hasValidContextCache;
|
||||
|
||||
private ThreadContext GetCurrentContext()
|
||||
{
|
||||
var now = DateTime.UtcNow.Ticks;
|
||||
|
||||
// Cache hit
|
||||
if (_hasValidContextCache && (now - _contextCacheTimestamp) < ContextCacheDurationTicks)
|
||||
{
|
||||
return _cachedContext;
|
||||
}
|
||||
|
||||
const int MaxRegistersAArch32 = 15;
|
||||
const int MaxFpuRegistersAArch32 = 16;
|
||||
|
||||
ThreadContext context = new();
|
||||
|
||||
Span<ulong> registersSpan = context.Registers.AsSpan();
|
||||
Span<V128> fpuRegistersSpan = context.FpuRegisters.AsSpan();
|
||||
|
||||
@@ -705,12 +718,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
{
|
||||
registersSpan[i] = Context.GetX(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fpuRegistersSpan.Length; i++)
|
||||
{
|
||||
fpuRegistersSpan[i] = Context.GetV(i);
|
||||
}
|
||||
|
||||
context.Fp = Context.GetX(29);
|
||||
context.Lr = Context.GetX(30);
|
||||
context.Sp = Context.GetX(31);
|
||||
@@ -724,12 +735,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
{
|
||||
registersSpan[i] = (uint)Context.GetX(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaxFpuRegistersAArch32; i++)
|
||||
{
|
||||
fpuRegistersSpan[i] = Context.GetV(i);
|
||||
}
|
||||
|
||||
context.Pc = (uint)Context.Pc;
|
||||
context.Pstate = GetPsr(Context);
|
||||
context.Tpidr = (uint)Context.TpidrroEl0;
|
||||
@@ -738,6 +747,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||
context.Fpcr = (uint)Context.Fpcr;
|
||||
context.Fpsr = (uint)Context.Fpsr;
|
||||
|
||||
// Update cache
|
||||
_cachedContext = context;
|
||||
_contextCacheTimestamp = now;
|
||||
_hasValidContextCache = true;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user