mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
[VideoOut] Fix sceVideoOutGetFlipStatus structure layout
flipArg was hardcoded to 0 and nothing recorded the argument passed to SubmitFlip, so a title waiting for its own flip label never observed it complete. currentBuffer was written as a u64 at 0x20, which is the submitTsc slot; the real field is a u32 at 0x38. Offsets 0x28-0x3C were never written at all, leaving the guest to read stale stack bytes as gcQueueNum/flipPendingNum. Record LastFlipArg on the port and write the full documented layout. TryWriteUInt32Compat becomes internal so the u32 tail can be written. Observed on DOOM + DOOM II (PPSA21444), which spun ~4.4M iterations of sceKernelWaitEqueue/sceVideoOutGetFlipStatus until the import-loop guard fired. The spin no longer occurs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5408,7 +5408,7 @@ public static partial class KernelMemoryCompatExports
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32Compat(CpuContext ctx, ulong address, uint value)
|
||||
internal static bool TryWriteUInt32Compat(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
|
||||
|
||||
@@ -197,6 +197,7 @@ public static class VideoOutExports
|
||||
public int FlipRate { get; set; }
|
||||
public ulong VblankCount { get; set; }
|
||||
public ulong FlipCount { get; set; }
|
||||
public long LastFlipArg { get; set; }
|
||||
public int CurrentBuffer { get; set; } = -1;
|
||||
public uint OutputWidth { get; set; } = 1920;
|
||||
public uint OutputHeight { get; set; } = 1080;
|
||||
@@ -709,18 +710,30 @@ public static class VideoOutExports
|
||||
}
|
||||
|
||||
ulong count;
|
||||
long flipArg;
|
||||
uint currentBuffer;
|
||||
lock (_stateGate)
|
||||
{
|
||||
count = port.FlipCount;
|
||||
flipArg = port.LastFlipArg;
|
||||
currentBuffer = unchecked((uint)port.CurrentBuffer);
|
||||
}
|
||||
|
||||
// SceVideoOutFlipStatus: count(0x00) processTime(0x08) tsc(0x10) flipArg(0x18)
|
||||
// submitTsc(0x20) reserved(0x28) gcQueueNum(0x30,u32) flipPendingNum(0x34,u32)
|
||||
// currentBuffer(0x38,u32) reserved(0x3C,u32).
|
||||
var tsc = unchecked((ulong)Stopwatch.GetTimestamp());
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x00, count);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x08, 0);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x10, 0);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x18, 0);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x20, currentBuffer);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x10, tsc);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x18, unchecked((ulong)flipArg));
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x20, tsc);
|
||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x28, 0);
|
||||
// No flips are ever left outstanding: SubmitFlip completes synchronously.
|
||||
KernelMemoryCompatExports.TryWriteUInt32Compat(ctx, statusAddress + 0x30, 0);
|
||||
KernelMemoryCompatExports.TryWriteUInt32Compat(ctx, statusAddress + 0x34, 0);
|
||||
KernelMemoryCompatExports.TryWriteUInt32Compat(ctx, statusAddress + 0x38, currentBuffer);
|
||||
KernelMemoryCompatExports.TryWriteUInt32Compat(ctx, statusAddress + 0x3C, 0);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
@@ -1159,6 +1172,7 @@ public static class VideoOutExports
|
||||
|
||||
port.CurrentBuffer = bufferIndex;
|
||||
port.FlipCount++;
|
||||
port.LastFlipArg = flipArg;
|
||||
eventHint = SceVideoOutInternalEventFlip |
|
||||
((unchecked((ulong)flipArg) & 0x0000_FFFF_FFFF_FFFFUL) << 16);
|
||||
flipEventCount = port.FlipEvents.Count;
|
||||
|
||||
Reference in New Issue
Block a user