mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-31 23:19:44 +08:00
* [HLE] Trigger AGC graphics events by filter instead of exact ident (#173) The PM4 EVENT_WRITE packet carries a 6-bit hardware EVENT_TYPE, but the guest registers AGC events via sceAgcDriverAddEqEvent with a full guest eventId. These two values are not the same numbering scheme, so the exact ident lookup in TriggerRegisteredEvents never matched and the AGC interrupt thread hung forever. Add TriggerRegisteredEventsByFilter, which wakes every graphics event registration on every queue. This is a compatibility workaround for issue #173 while the real PS5 mapping remains unknown. Includes unit tests covering the mismatched ident/eventType case. * [HLE] Fix POSIX condition variable semantics (#113) Remove PendingSignals from PthreadCondState. POSIX condition signals are edges, not semaphore credits - a signal with no waiter must have no effect. The previous implementation persisted signals, causing lock inversions and predicate bypasses. Changes: - Remove PendingSignals property and TryConsumePendingSignal method - Remove pending signal consumption logic from PthreadCondWaitCore - Remove PendingSignals increment from PthreadCondSignalCore - Add regression tests verifying POSIX-correct behavior Fixes #113
This commit is contained in:
@@ -2741,8 +2741,12 @@ public static class AgcExports
|
||||
ctx.TryReadUInt32(currentAddress + sizeof(uint), out var eventTypeRaw))
|
||||
{
|
||||
var eventType = eventTypeRaw & 0x3Fu;
|
||||
var triggered = KernelEventQueueCompatExports.TriggerRegisteredEvents(
|
||||
eventType,
|
||||
|
||||
// The guest registers AGC events with a full eventId, but the command buffer
|
||||
// only carries a 6-bit EVENT_TYPE. Those two values are not the same numbering
|
||||
// scheme, so exact ident matching never wakes anything. Trigger every graphics
|
||||
// event registration instead (workaround for issue #173).
|
||||
var triggered = KernelEventQueueCompatExports.TriggerRegisteredEventsByFilter(
|
||||
KernelEventQueueCompatExports.KernelEventFilterGraphics,
|
||||
eventType);
|
||||
if (tracePackets)
|
||||
|
||||
@@ -589,6 +589,66 @@ public static class KernelEventQueueCompatExports
|
||||
return triggeredCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers every registered event on every queue that matches <paramref name="filter"/>
|
||||
/// regardless of the registration's <c>ident</c>. This is a workaround for PS5 AGC command
|
||||
/// buffers, where <c>IT_EVENT_WRITE</c> carries a hardware <c>EVENT_TYPE</c> that does not
|
||||
/// match the <c>eventId</c> the guest registered with <c>sceAgcDriverAddEqEvent</c>.
|
||||
/// See issue #173.
|
||||
/// </summary>
|
||||
public static int TriggerRegisteredEventsByFilter(
|
||||
short filter,
|
||||
ulong data)
|
||||
{
|
||||
List<ulong>? wakeHandles = null;
|
||||
var triggeredCount = 0;
|
||||
lock (_eventQueueGate)
|
||||
{
|
||||
foreach (var (handle, registrations) in _registeredEvents)
|
||||
{
|
||||
foreach (var registration in registrations.Values)
|
||||
{
|
||||
if (registration.Filter != filter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_pendingEvents.TryGetValue(handle, out var queue))
|
||||
{
|
||||
queue = new KernelEventDeque();
|
||||
_pendingEvents[handle] = queue;
|
||||
}
|
||||
|
||||
QueueOrUpdateEvent(
|
||||
queue,
|
||||
new KernelQueuedEvent(
|
||||
registration.Ident,
|
||||
registration.Filter,
|
||||
0,
|
||||
1,
|
||||
data,
|
||||
registration.UserData));
|
||||
(wakeHandles ??= new List<ulong>()).Add(handle);
|
||||
triggeredCount++;
|
||||
|
||||
// A single queue only needs to be woken once, even if multiple
|
||||
// registrations matched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wakeHandles is not null)
|
||||
{
|
||||
foreach (var handle in wakeHandles)
|
||||
{
|
||||
WakeEventQueue(handle);
|
||||
}
|
||||
}
|
||||
|
||||
return triggeredCount;
|
||||
}
|
||||
|
||||
private static bool TriggerRegisteredEvent(
|
||||
ulong handle,
|
||||
ulong ident,
|
||||
|
||||
@@ -99,20 +99,6 @@ public static class KernelPthreadCompatExports
|
||||
|
||||
// See PthreadMutexState.WakeKey.
|
||||
public string WakeKey { get; } = "pthread_cond#" + Interlocked.Increment(ref _nextCondWakeId).ToString("X");
|
||||
|
||||
// A signal with no waiter stays pending; the guest often signals before the wait.
|
||||
public int PendingSignals { get; set; }
|
||||
|
||||
public bool TryConsumePendingSignal()
|
||||
{
|
||||
if (PendingSignals <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PendingSignals--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly record struct PthreadMutexAttrState(int Type, int Protocol);
|
||||
@@ -1375,9 +1361,8 @@ public static class KernelPthreadCompatExports
|
||||
{
|
||||
state.Waiters++;
|
||||
var observedEpoch = state.SignalEpoch;
|
||||
var consumedPendingSignal = state.TryConsumePendingSignal();
|
||||
TracePthreadCond(
|
||||
consumedPendingSignal ? "wait-enter-pending" : "wait-enter",
|
||||
"wait-enter",
|
||||
condAddress,
|
||||
mutexAddress,
|
||||
state,
|
||||
@@ -1392,25 +1377,6 @@ public static class KernelPthreadCompatExports
|
||||
return unlockResult;
|
||||
}
|
||||
|
||||
if (consumedPendingSignal)
|
||||
{
|
||||
state.Waiters = Math.Max(0, state.Waiters - 1);
|
||||
TracePthreadCond("wait-wake-pending", condAddress, mutexAddress, state, timed, waitResult);
|
||||
|
||||
// Relock outside SyncRoot to preserve lock ordering.
|
||||
Monitor.Exit(state.SyncRoot);
|
||||
try
|
||||
{
|
||||
var pendingLockResult = PthreadMutexRelockForCondWait(ctx, mutexAddress, releasedRecursion);
|
||||
return pendingLockResult != (int)OrbisGen2Result.ORBIS_GEN2_OK ? pendingLockResult : waitResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Balance the surrounding lock statement.
|
||||
Monitor.Enter(state.SyncRoot);
|
||||
}
|
||||
}
|
||||
|
||||
var scheduler = GuestThreadExecution.Scheduler;
|
||||
if (GuestThreadExecution.IsGuestThread &&
|
||||
GuestThreadExecution.RequestCurrentThreadBlock(
|
||||
@@ -1542,10 +1508,6 @@ public static class KernelPthreadCompatExports
|
||||
Monitor.Pulse(state.SyncRoot);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.PendingSignals++;
|
||||
}
|
||||
|
||||
TracePthreadCond(broadcast ? "broadcast" : "signal", condAddress, mutexAddress: 0, state, timed: false, (int)OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user