mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-26 04:39:17 +08:00
864cbb0fa0
* [Core] Add POSIX native execution and PS5 SELF support Extend the native backend, guest TLS, fixed-address memory, and loader paths needed by PS5 titles on Windows, Linux, and macOS. Keep workstation GC so high-core-count hosts do not reserve over fixed guest image bases. * [HLE] Expand PS5 service and media compatibility Add the kernel, threading, save-data, networking, audio, video-codec, font, dialog, and service exports required by newer PS5 software. Preserve every SysAbi NID currently registered by main while adding the compatibility surface used by ASTRO BOT. * [AGC/Vulkan] Extend Gen5 shader and presentation support Expand PM4 handling, Gen5 shader translation, MRT and packed export support, guest image tracking, depth initialization, texture aliasing, and Vulkan presentation. Add the performance overlay and address-filtered diagnostics used to validate ASTRO BOT with original shaders. * [Core] Align static TLS reservation across hosts * [Pad] Align primary user ID with UserService * [Gpu] Preserve runtime scalar buffers across renderer seam * [AGC] Restore omitted command helper exports * [Vulkan] Reuse primary views for promoted MRT targets * [Vulkan] Preserve scratch storage bindings in compute dispatches
83 lines
2.7 KiB
C#
83 lines
2.7 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Diagnostics;
|
|
|
|
namespace SharpEmu.Libs;
|
|
|
|
/// <summary>
|
|
/// High-resolution host sleeps for guest pacing. <see cref="Thread.Sleep(int)"/>
|
|
/// routinely overshoots by a scheduler quantum, which turns per-frame waits
|
|
/// (flip pacing, usleep-based game loops) into a hard frame-rate cap; these
|
|
/// helpers sleep coarsely for the bulk of the interval and yield-spin the
|
|
/// remainder so wakeups land within tens of microseconds of the target.
|
|
/// </summary>
|
|
internal static class HostTiming
|
|
{
|
|
/// <summary>
|
|
/// Blocks until <see cref="Stopwatch.GetTimestamp"/> reaches
|
|
/// <paramref name="targetTimestamp"/>.
|
|
/// </summary>
|
|
public static void SleepUntil(long targetTimestamp)
|
|
{
|
|
while (true)
|
|
{
|
|
var remainingTicks = targetTimestamp - Stopwatch.GetTimestamp();
|
|
if (remainingTicks <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (remainingTicks > Stopwatch.Frequency * 60)
|
|
{
|
|
// Far-future target: coarse sleep avoids overflowing the
|
|
// microsecond conversion below and needs no precision.
|
|
Thread.Sleep(30_000);
|
|
continue;
|
|
}
|
|
|
|
var remainingMicroseconds = remainingTicks * 1_000_000 / Stopwatch.Frequency;
|
|
if (remainingMicroseconds > 2200)
|
|
{
|
|
// Coarse sleep for the bulk; macOS overshoots ~0.5-1.5 ms.
|
|
Thread.Sleep((int)((remainingMicroseconds - 1200) / 1000));
|
|
}
|
|
else if (remainingMicroseconds > 1200)
|
|
{
|
|
// A 1 ms nap typically wakes within the margin and costs no
|
|
// CPU, unlike yield-spinning through the whole tail.
|
|
Thread.Sleep(1);
|
|
}
|
|
else if (remainingMicroseconds > 100)
|
|
{
|
|
Thread.Sleep(0);
|
|
}
|
|
else
|
|
{
|
|
Thread.SpinWait(64);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Blocks for the given number of microseconds.</summary>
|
|
public static void SleepMicroseconds(long microseconds)
|
|
{
|
|
if (microseconds <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (microseconds >= 10_000_000)
|
|
{
|
|
// Long/sentinel sleeps (usleep(-1) parks): sub-millisecond
|
|
// precision is irrelevant and the tick conversion below would
|
|
// overflow, which used to turn the park into a hot spin.
|
|
Thread.Sleep((int)Math.Min(microseconds / 1000, int.MaxValue));
|
|
return;
|
|
}
|
|
|
|
var ticks = microseconds * Stopwatch.Frequency / 1_000_000;
|
|
SleepUntil(Stopwatch.GetTimestamp() + ticks);
|
|
}
|
|
}
|