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
+48 -30
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Logging;
using SharpEmu.HLE.Host;
using SharpEmu.Libs.Diagnostics;
using SharpEmu.Libs.Gpu;
@@ -69,11 +70,14 @@ public static class VideoOutExports
private static readonly Dictionary<int, VideoOutPortState> _ports = new();
private static int _presentationWindowCloseNotified;
private static int _vblankStopRequested;
private static int _hdrOutputRequested;
private static readonly Dictionary<(int Handle, int BufferIndex, ulong Address), ulong> _lastFrameFingerprints = new();
private static int _nextHandle = 1;
private static int _frameDumpCount;
private static long _nextFrameDumpIndex;
private static string _windowTitle = "SharpEmu VideoOut";
private static string _applicationWindowTitle = "VideoOut";
private static string _selectedGpuName = string.Empty;
private static string _applicationTitleId = "UNKNOWN";
private static readonly bool _logFrameRate = string.Equals(
Environment.GetEnvironmentVariable("SHARPEMU_LOG_VIDEOOUT_FPS"),
"1",
@@ -113,7 +117,18 @@ public static class VideoOutExports
var versionSuffix = string.IsNullOrWhiteSpace(version) ? string.Empty : $" v{version.Trim()}";
lock (_stateGate)
{
_windowTitle = $"SharpEmu - {application}{versionSuffix}";
_applicationTitleId = string.IsNullOrWhiteSpace(titleId)
? "UNKNOWN"
: titleId.Trim();
_applicationWindowTitle = $"{application}{versionSuffix}";
}
}
internal static string GetApplicationTitleId()
{
lock (_stateGate)
{
return _applicationTitleId;
}
}
@@ -121,7 +136,10 @@ public static class VideoOutExports
{
lock (_stateGate)
{
return _windowTitle;
var gpuSuffix = string.IsNullOrWhiteSpace(_selectedGpuName)
? string.Empty
: $" · {_selectedGpuName}";
return $"SharpEmu · {BuildInfo.CommitSha ?? "dev"} - {_applicationWindowTitle}{gpuSuffix}";
}
}
@@ -139,7 +157,7 @@ public static class VideoOutExports
: string.Empty;
lock (_stateGate)
{
_windowTitle = $"{_windowTitle} · {gpuName.Trim()}{backendSuffix}";
_selectedGpuName = $"{gpuName.Trim()}{backendSuffix}";
}
}
@@ -166,30 +184,17 @@ public static class VideoOutExports
private static void RequestHostShutdown(string reason)
{
Console.Error.WriteLine($"[LOADER][INFO] Host shutdown requested: {reason}");
var embedded = VulkanVideoHost.IsEmbedded;
AudioOutExports.ShutdownAllPorts();
Interlocked.Exchange(ref _vblankStopRequested, 1);
HostSessionControl.RequestShutdown(reason);
GuestGpu.Current.RequestClose();
// A hosted game can still be issuing AGC work after it requests its
// own shutdown. Keep the presenter's resources alive until the GUI
// session reaches its guest-safe exit path and disposes the host
// surface.
if (!embedded)
// Give guest and GPU threads a bounded window to leave cooperatively.
ThreadPool.QueueUserWorkItem(static _ =>
{
GuestGpu.Current.RequestClose();
}
// The embedded GUI owns the process lifetime. A guest shutdown should
// end only that session rather than terminating the launcher itself.
if (!embedded)
{
ThreadPool.QueueUserWorkItem(static _ =>
{
Thread.Sleep(2000);
Environment.Exit(0);
});
}
Thread.Sleep(2000);
Environment.Exit(0);
});
}
private sealed class VideoOutPortState
@@ -871,6 +876,8 @@ public static class VideoOutExports
}
}
internal static bool IsHdrOutputRequested => Volatile.Read(ref _hdrOutputRequested) != 0;
[SysAbiExport(
Nid = "MTxxrOCeSig",
ExportName = "sceVideoOutSetWindowModeMargins",
@@ -1175,16 +1182,21 @@ public static class VideoOutExports
var guestImageSubmitted = false;
ulong guestImageAddress = 0;
if (submitGpuImage &&
bufferIndex >= 0 &&
if (bufferIndex >= 0 &&
TryGetDisplayBufferInfo(handle, bufferIndex, out var displayBuffer))
{
Interlocked.Exchange(
ref _hdrOutputRequested,
IsHdrPixelFormat(displayBuffer.PixelFormat) ? 1 : 0);
guestImageAddress = displayBuffer.Address;
guestImageSubmitted = GuestGpu.Current.TrySubmitGuestImage(
displayBuffer.Address,
displayBuffer.Width,
displayBuffer.Height,
displayBuffer.PitchInPixel);
if (submitGpuImage)
{
guestImageSubmitted = GuestGpu.Current.TrySubmitGuestImage(
displayBuffer.Address,
displayBuffer.Width,
displayBuffer.Height,
displayBuffer.PitchInPixel);
}
}
if (_dumpVideoOut)
@@ -1711,6 +1723,12 @@ public static class VideoOutExports
internal static bool IsPacked10BitPixelFormat(ulong pixelFormat) =>
IsPacked10BitPixelFormatNormalized(NormalizePixelFormat(pixelFormat));
internal static bool IsHdrPixelFormat(ulong pixelFormat) =>
NormalizePixelFormat(pixelFormat) is
SceVideoOutPixelFormatA2R10G10B10Bt2020Pq or
SceVideoOutPixelFormat2R10G10B10A2Bt2100Pq or
SceVideoOutPixelFormat2B10G10R10A2Bt2100Pq;
private static bool IsPacked10BitPixelFormatNormalized(ulong pixelFormat) =>
pixelFormat is
SceVideoOutPixelFormatA2R10G10B10 or