mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-23 03:16:22 +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;
|
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)];
|
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
|
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ public static class VideoOutExports
|
|||||||
public int FlipRate { get; set; }
|
public int FlipRate { get; set; }
|
||||||
public ulong VblankCount { get; set; }
|
public ulong VblankCount { get; set; }
|
||||||
public ulong FlipCount { get; set; }
|
public ulong FlipCount { get; set; }
|
||||||
|
public long LastFlipArg { get; set; }
|
||||||
public int CurrentBuffer { get; set; } = -1;
|
public int CurrentBuffer { get; set; } = -1;
|
||||||
public uint OutputWidth { get; set; } = 1920;
|
public uint OutputWidth { get; set; } = 1920;
|
||||||
public uint OutputHeight { get; set; } = 1080;
|
public uint OutputHeight { get; set; } = 1080;
|
||||||
@@ -709,18 +710,30 @@ public static class VideoOutExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong count;
|
ulong count;
|
||||||
|
long flipArg;
|
||||||
uint currentBuffer;
|
uint currentBuffer;
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
{
|
{
|
||||||
count = port.FlipCount;
|
count = port.FlipCount;
|
||||||
|
flipArg = port.LastFlipArg;
|
||||||
currentBuffer = unchecked((uint)port.CurrentBuffer);
|
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 + 0x00, count);
|
||||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x08, 0);
|
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x08, 0);
|
||||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x10, 0);
|
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x10, tsc);
|
||||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x18, 0);
|
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x18, unchecked((ulong)flipArg));
|
||||||
KernelMemoryCompatExports.TryWriteUInt64Compat(ctx, statusAddress + 0x20, currentBuffer);
|
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;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1159,6 +1172,7 @@ public static class VideoOutExports
|
|||||||
|
|
||||||
port.CurrentBuffer = bufferIndex;
|
port.CurrentBuffer = bufferIndex;
|
||||||
port.FlipCount++;
|
port.FlipCount++;
|
||||||
|
port.LastFlipArg = flipArg;
|
||||||
eventHint = SceVideoOutInternalEventFlip |
|
eventHint = SceVideoOutInternalEventFlip |
|
||||||
((unchecked((ulong)flipArg) & 0x0000_FFFF_FFFF_FFFFUL) << 16);
|
((unchecked((ulong)flipArg) & 0x0000_FFFF_FFFF_FFFFUL) << 16);
|
||||||
flipEventCount = port.FlipEvents.Count;
|
flipEventCount = port.FlipEvents.Count;
|
||||||
|
|||||||
Reference in New Issue
Block a user