From e99fcb9651a179a367005dca0cc344b7b29b2b99 Mon Sep 17 00:00:00 2001 From: kuba Date: Sun, 27 Sep 2026 01:01:05 +0200 Subject: [PATCH] [Cpu] Give guest execution threads a 64 MiB host stack for nested continuations Every guest call that blocks in an HLE import resumes through a managed continuation frame, so a guest thread that blocks deep inside its own call tree nests host frames instead of unwinding them. Demon's Souls' render thread overflowed the default thread stack within a minute of reaching its main loop. Guest execution threads now reserve 64 MiB. The reservation is virtual, so a generous stack costs nothing until it is touched. --- src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs index 3af1142..ee70d5a 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs @@ -608,9 +608,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private Action? _work; private volatile bool _stopping; + // Every guest call that blocks in an HLE import resumes through a managed + // continuation frame, so a guest thread that blocks deep inside its own call + // tree nests host frames rather than unwinding. Demon's Souls' render thread + // overflowed the default thread stack within a minute of reaching its main + // loop; the reservation is virtual, so a generous stack costs nothing until + // it is touched. + private const int GuestExecutionStackBytes = 64 * 1024 * 1024; + public GuestExecutionRunner(ulong guestThreadHandle, string name, ThreadPriority priority) { - _thread = new Thread(() => ThreadMain(guestThreadHandle)) + _thread = new Thread(() => ThreadMain(guestThreadHandle), GuestExecutionStackBytes) { IsBackground = true, Name = $"SharpEmu-{name}",