72 lines
2.5 KiB
C#
72 lines
2.5 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.Libs.Agc;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.Agc;
|
|
|
|
public sealed class GpuWaitRegistryProducedRetentionTests
|
|
{
|
|
private const ulong WatchedLabel = 0x7020_0000_1000UL;
|
|
|
|
// A suspended DCB whose label the guest has recycled can only be released by
|
|
// replaying the value a real producer wrote to that label. Recording enough
|
|
// unrelated producers to cross the table's soft bound must not discard that
|
|
// value, or the waiter is stranded and the graphics queue never resumes.
|
|
[Fact]
|
|
public void ProducedValueSurvivesBoundCrossingWhileAWaiterWatchesIt()
|
|
{
|
|
GpuWaitRegistry.Clear();
|
|
var memory = new object();
|
|
|
|
GpuWaitRegistry.Register(WatchedLabel, NewWaiter(memory, WatchedLabel));
|
|
Assert.True(GpuWaitRegistry.RecordProduced(memory, WatchedLabel, 1));
|
|
|
|
// Cross the soft bound with labels nobody is waiting on.
|
|
for (var i = 0; i < 9000; i++)
|
|
{
|
|
GpuWaitRegistry.RecordProduced(memory, 0x7030_0000_0000UL + ((ulong)i * 8), 1);
|
|
}
|
|
|
|
// The guest has since recycled the label, so its memory no longer holds
|
|
// the produced value — the registry's record is the only way back.
|
|
var broken = GpuWaitRegistry.CollectDeadlockBroken(memory, nowTicks: 1_000_000, minAgeTicks: 1);
|
|
|
|
Assert.NotNull(broken);
|
|
Assert.Contains(broken!, waiter => waiter.WaitAddress == WatchedLabel);
|
|
GpuWaitRegistry.Clear();
|
|
}
|
|
|
|
[Fact]
|
|
public void UnwatchedProducedValuesArePrunedAtTheBound()
|
|
{
|
|
GpuWaitRegistry.Clear();
|
|
var memory = new object();
|
|
|
|
for (var i = 0; i < 9000; i++)
|
|
{
|
|
GpuWaitRegistry.RecordProduced(memory, 0x7030_0000_0000UL + ((ulong)i * 8), 1);
|
|
}
|
|
|
|
// Nothing was watching any of them, so a waiter registered afterwards on
|
|
// a pruned label has no produced value to replay and stays suspended.
|
|
GpuWaitRegistry.Register(WatchedLabel, NewWaiter(memory, WatchedLabel));
|
|
var broken = GpuWaitRegistry.CollectDeadlockBroken(memory, nowTicks: 1_000_000, minAgeTicks: 1);
|
|
|
|
Assert.Null(broken);
|
|
GpuWaitRegistry.Clear();
|
|
}
|
|
|
|
private static GpuWaitRegistry.WaitingDcb NewWaiter(object memory, ulong address) => new()
|
|
{
|
|
WaitAddress = address,
|
|
ReferenceValue = 1,
|
|
Mask = 0xFFFF_FFFFUL,
|
|
CompareFunction = 3, // equal
|
|
Memory = memory,
|
|
QueueName = "dcb.graphics",
|
|
RegisteredTicks = 0,
|
|
};
|
|
}
|