diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs index f13662e44..979806a18 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContext.cs @@ -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 /// 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); /// 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() { } } } diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs index 9ef03e61e..9f313f2f0 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs @@ -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) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index aaa4ccd99..4f06752cc 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -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 registersSpan = context.Registers.AsSpan(); Span 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; }