Astro Bot stack: VEH/TBB, title clear, swapchain fallback, Psml MFSR (#528)

* Cpu/Kernel: harden VEH trampoline and keep TBB on native workers

Route FastFail/CLR/stack-overflow around managed VEH, serialize managed
entry with a recursive spinlock, require native workers for tbb_thead,
and abandon pthread mutexes when a guest thread is torn down by worker
abort so splash waiters are not left holding locks forever.

* Cpu: soft-fail TBB native worker storms and cap concurrent Runs

Throwing on worker/prologue faults killed the process mid tbb_thead
burst (FailFast 0xC0000409). Soft-return 0x80020012, limit in-flight
native Runs (default 2), and keep prewarm small so back-to-back boots
do not need an artificial settle delay.

* Agc/VideoOut: poison-only empty-SRT reject and clear procedural ES/PS

Skip QueueSubmit only when Address-0 image slots remain; run the
Astro title clear pair via CmdClearColorImage so the pass executes
without descriptors that lose the device.

* VideoOut: recreate swapchain with fallback extent on 0x0 surface

Minimized Win32 surfaces report 0x0 / MaxImageExtent=0; deferring
recreate forever left an OutOfDate swapchain with no presents.
Clamp to last/default size and recreate instead of early-return.

* Psml: stub MFSR init/shared/context and dispatch packet size

Astro Bot asserts in GfxRenderStagePSSR when scePsmlMfsrInit is unresolved
(Mfsr initialized failed). Soft HLE for the MFSR shared-resource and
800M3_2 context path plus dispatch packet size lets boot pass splash to
first frame without claiming real upscaling.

* Psml: stub MFSR GetDispatchMfsrPacket900 for logo PSSR

Astro StartLevel ps_logo asserted GfxRenderStagePSSR.cpp:266 when
GetDispatchMfsrPacket900 (RUNLFro+qok) was unresolved. Return SCE_OK
from SizeInDwords so the 900 fill runs, soft-clear the guest packet
buffer, and register 1000/1100 siblings for the same ABI.
This commit is contained in:
Mike Saito
2026-07-23 12:37:44 +03:00
committed by GitHub
parent 7a108c6f87
commit 96fde5764f
13 changed files with 2472 additions and 162 deletions
@@ -4,6 +4,7 @@
using SharpEmu.HLE;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Numerics;
@@ -35,6 +36,14 @@ public static class Gen5ShaderScalarEvaluator
StringComparison.Ordinal);
private static readonly object _scalarFallbackTraceGate = new();
private static readonly HashSet<(ulong Shader, uint Pc)> _tracedScalarFallbacks = [];
// Shaders whose empty SRT/EUD caused a null-base scalar pointer load.
// Host submit of those translations has lost the Vulkan device; Agc skips
// them before QueueSubmit.
private static readonly ConcurrentDictionary<ulong, byte> _emptySrtScalarPointerFallbacks =
new();
public static bool WasEmptySrtScalarPointerFallback(ulong shaderAddress) =>
_emptySrtScalarPointerFallbacks.ContainsKey(shaderAddress);
// Uniform forward branches select material/resource bodies that remain
// statically present in the translated shader. Discover the skipped body's
@@ -2101,6 +2110,15 @@ public static class Gen5ShaderScalarEvaluator
$"dynamic={dynamicOffset} definitions=[{string.Join(';', definitions)}] " +
$"user_data=[{userData}] metadata=" +
$"{(state.Metadata is null ? "missing" : $"srt={state.Metadata.ShaderResourceTableSizeDwords},eud={state.Metadata.ExtendedUserDataSizeDwords}")}");
if (baseAddress == 0 &&
state.Metadata is
{
ShaderResourceTableSizeDwords: 0,
ExtendedUserDataSizeDwords: 0,
})
{
_emptySrtScalarPointerFallbacks.TryAdd(state.Program.Address, 0);
}
}
[Conditional("DEBUG")]
@@ -305,6 +305,23 @@ public static class Gen5ShaderTranslator
count |= 0x20;
}
// Primary SH defaults leave SPI_SHADER_PGM_RSRC2_PS at 0. Draws that
// still wrote USER_DATA_n via SetShReg would otherwise translate with
// an empty SRT window (Astro title PS → Address-0 descriptors →
// device lost). Recover the window from contiguous live registers.
if (count == 0 &&
userDataBaseRegister is not ComputeUserDataRegister)
{
var probed = 0;
while (probed < MaximumHardwareUserSgprs &&
shaderRegisters.ContainsKey(userDataBaseRegister + (uint)probed))
{
probed++;
}
count = probed;
}
if (userDataBaseRegister is not (PsUserDataRegister or
VsUserDataRegister or
GsUserDataRegister or