Sdl backend (#670)

* [audio] added sdl audio backend and in-tree atrac9 decoder

* [input] replaced per-platform pad readers with sdl gamepad input

* [video] added sdl window and host display plumbing

* [gui] added host display options and per-game render settings

* [bink] synced host movie playback to the guest audio clock

* [cpu] hooked windows write faults into guest image tracking

* [perf] added guest and render profiling, reserved host cpu lanes

* [kernel] fixed stale pthread mutex handle alias

* [host] wired the sdl session, save-data paths and project references

* [audio] hoisted ajm trace stackalloc out of its loop

* [video] Add guest image sync setting

* [build] Strip native symbols

* reuse
This commit is contained in:
Berk
2026-07-28 03:33:26 +03:00
committed by GitHub
parent b4cc5f88ca
commit 2b6bd5a532
111 changed files with 9846 additions and 4479 deletions
@@ -5314,7 +5314,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
var hostCpu = processorCount < 8
? guestCpu % processorCount
: processorCount >= 16
? guestCpu * 2
? MapGuestCpuAcrossSmtLanes(guestCpu, processorCount)
: guestCpu;
if (hostCpu < processorCount)
{
@@ -5325,6 +5325,45 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
return hostAffinityMask;
}
/// <summary>
/// Places guest CPUs on distinct physical cores first, then wraps onto the
/// SMT siblings. Doubling the index alone only works while the title stays
/// inside the first half of the guest CPU set: beyond that every mapped lane
/// lands past the host's processor count and gets dropped, which silently
/// leaves those threads unpinned. Demon's Souls asks for CPUs 0-12 and keeps
/// its renderer on 9 and 11, so dropping the overflow un-pinned both the
/// renderer and a third of its job pool onto every core at once.
/// </summary>
private static int MapGuestCpuAcrossSmtLanes(int guestCpu, int processorCount)
{
// Reserve the top lanes for the emulator itself. A title sized for a
// console's dedicated cores will happily keep a worker per guest CPU
// spinning on an empty queue — Demon's Souls' job pool runs ~90% busy
// doing nothing — and spreading those across every host lane leaves the
// GPU translation and present threads fighting them for a slice. Packing
// near-idle spinners tighter costs them almost nothing and buys back
// whole cores for the work that actually produces frames.
var usableLanes = Math.Max(processorCount - EmulatorReservedLanes, 2);
var physicalCores = usableLanes / 2;
var lane = guestCpu % usableLanes;
return lane < physicalCores
? lane * 2
: ((lane - physicalCores) * 2) + 1;
}
/// <summary>
/// Host lanes kept away from guest threads. Measured on a 16-lane host with
/// Demon's Souls: reserving 0/4/6/8 lanes gave 6.08/6.78/7.20/5.62 fps, so
/// the useful range is a bit over a third of the machine — too few and the
/// emulator is crowded out, too many and the guest cannot make progress.
/// </summary>
private static readonly int EmulatorReservedLanes =
int.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_RESERVED_HOST_LANES"),
out var reserved) && reserved >= 0
? reserved
: Math.Max(2, Environment.ProcessorCount * 3 / 8);
public bool TrySetGuestThreadPriority(ulong guestThreadHandle, int guestPriority)
{
lock (_guestThreadGate)