mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
e1cf5b13ef
* [agc] WAIT_REG_MEM suspend/resume, draw packet fixes, new HLE exports, debug cleanup
Rebased onto upstream 79a7437 (par274/sharpemu, rewritten history).
- GpuWaitRegistry: DCBs suspended on unsatisfied WAIT_REG_MEM are re-polled
against guest memory on every submit; fixed 64-bit and standard packet parse
offsets, apply the mask, treat PM4 compare function 0 as "always".
- TryReadSubmittedDrawCount: accept the 5-dword ItDrawIndex2 form emitted by
DcbDrawIndex (count at +4); menu draws were silently discarded before.
- sceAgcDriverSubmitMultiDcbs: reversed ABI (rdi=address array, rsi=dword
sizes, rdx=count).
- VideoOut: vblank events, sceVideoOutGetFlipStatus, buffers registered via
sceVideoOutRegisterBuffers are valid flip targets.
- New HLE: libc stdio (fopen/fread/fseek/ftell/fclose/fgets), Dinkumware
_Getpctype ctype table, NpTrophy2 stubs, AMPR PAK sequential-read tracker,
MsgDialog lifecycle, NGS2 alt NIDs + dummy vtable for handle objects,
guarded memset intrinsic, abort()/strcasecmp null-arg recovery.
- Removed investigation-only code (INT3 breakpoints, qfont/mcpp dumps,
error-candidate printf traces, unconditional debug logs).
First rendered frame: Quake (PPSA01880) presents a 1920x1080 guest frame.
* Implemented a guarded native intrinsic (rep movsb) in DirectExecutionBackend to bypass HLE dispatch overhead, while preserving memory safety checks.
103 lines
3.3 KiB
C#
103 lines
3.3 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
namespace SharpEmu.Libs.Agc;
|
|
|
|
// Holds DCBs whose parsing was suspended on an unsatisfied WAIT_REG_MEM condition.
|
|
// AgcExports re-checks every waiter against guest memory on each submit and resumes
|
|
// the ones whose condition became true (labels are advanced by ReleaseMem/WriteData/
|
|
// DmaData packets or by direct CPU writes).
|
|
internal static class GpuWaitRegistry
|
|
{
|
|
public struct WaitingDcb
|
|
{
|
|
public ulong CommandBufferAddress;
|
|
public ulong ResumeAddress;
|
|
public uint TotalDwords;
|
|
public uint ResumeOffset;
|
|
public ulong WaitAddress;
|
|
public ulong ReferenceValue;
|
|
public ulong Mask;
|
|
public uint CompareFunction;
|
|
public bool Is64Bit;
|
|
public object? State;
|
|
}
|
|
|
|
private static readonly object _gate = new();
|
|
private static readonly Dictionary<ulong, List<WaitingDcb>> _waiters = new();
|
|
|
|
public static void Register(ulong address, WaitingDcb waiter)
|
|
{
|
|
waiter.WaitAddress = address;
|
|
lock (_gate)
|
|
{
|
|
if (!_waiters.TryGetValue(address, out var list))
|
|
{
|
|
list = new List<WaitingDcb>();
|
|
_waiters.Add(address, list);
|
|
}
|
|
|
|
list.Add(waiter);
|
|
}
|
|
}
|
|
|
|
// Re-evaluates every registered waiter. readValue receives (address, is64Bit) and
|
|
// returns null when the memory is unreadable; such waiters are kept registered.
|
|
public static List<WaitingDcb>? CollectSatisfied(Func<ulong, bool, ulong?> readValue)
|
|
{
|
|
List<WaitingDcb>? woken = null;
|
|
lock (_gate)
|
|
{
|
|
List<ulong>? emptied = null;
|
|
foreach (var (address, list) in _waiters)
|
|
{
|
|
for (var i = list.Count - 1; i >= 0; i--)
|
|
{
|
|
var value = readValue(address, list[i].Is64Bit);
|
|
if (value is null || !Compare(list[i], value.Value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
woken ??= new List<WaitingDcb>();
|
|
woken.Add(list[i]);
|
|
list.RemoveAt(i);
|
|
}
|
|
|
|
if (list.Count == 0)
|
|
{
|
|
emptied ??= new List<ulong>();
|
|
emptied.Add(address);
|
|
}
|
|
}
|
|
|
|
if (emptied != null)
|
|
{
|
|
foreach (var address in emptied)
|
|
{
|
|
_waiters.Remove(address);
|
|
}
|
|
}
|
|
}
|
|
|
|
return woken;
|
|
}
|
|
|
|
public static bool Compare(in WaitingDcb waiter, ulong value)
|
|
{
|
|
var masked = value & waiter.Mask;
|
|
return waiter.CompareFunction switch
|
|
{
|
|
1 => masked < waiter.ReferenceValue,
|
|
2 => masked <= waiter.ReferenceValue,
|
|
3 => masked == waiter.ReferenceValue,
|
|
4 => masked != waiter.ReferenceValue,
|
|
5 => masked >= waiter.ReferenceValue,
|
|
6 => masked > waiter.ReferenceValue,
|
|
// 0 is "always" in the PM4 encoding and 7 is reserved; treating both as
|
|
// satisfied keeps a malformed packet from suspending its DCB forever.
|
|
_ => true,
|
|
};
|
|
}
|
|
}
|