[Tests] Cover ordered video-out state changes

Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com>
This commit is contained in:
ParantezTech 2026-10-06 17:47:35 +03:00
parent db05345cde
commit 2cecb5c552
2 changed files with 189 additions and 2 deletions

View File

@ -154,6 +154,83 @@ public sealed class CommandStreamQueueTests
Assert.Equal(new[] { "begin 0 1", "begin 1 3", "begin 0 2" }, host.Calls.Where(call => call.StartsWith("begin", StringComparison.Ordinal)));
}
[Fact]
public void ControlBarrier_RunsBetweenTheAcceptedSliceAndLaterAdmissions()
{
var (host, queue) = NewQueue();
Enqueue(host, queue, Graphics, 1, CreateInstanceCountPacket(1));
var cancelled = false;
host.PendingCommands.Enqueue(() =>
{
Assert.True(queue.TryEnqueueControlBarrier(
() => host.Calls.Add("mapping_change"),
() => cancelled = true));
Enqueue(host, queue, Graphics + 0x100, 2, CreateInstanceCountPacket(2));
});
Assert.Equal(SliceResult.Completed, queue.ProcessOne());
Assert.DoesNotContain("mapping_change", host.Calls);
Assert.Equal(SliceResult.Completed, queue.ProcessOne());
Assert.Equal(SliceResult.Completed, queue.ProcessOne());
var first = host.Calls.IndexOf("begin 0 1");
var barrier = host.Calls.IndexOf("mapping_change");
var second = host.Calls.IndexOf("begin 0 2");
Assert.True(first >= 0 && barrier > first && second > barrier, string.Join(", ", host.Calls));
Assert.False(cancelled);
}
[Fact]
public void ControlBarrier_PreservesCrossQueueProgressWhileAnOlderHeadIsBlocked()
{
var (host, queue) = NewQueue();
host.WriteDword(Label, 0);
Enqueue(host, queue, Graphics, 1, WaitEqual(Label, 1));
Assert.Equal(SliceResult.BlockedWithoutProgress, queue.ProcessOne());
Assert.True(queue.TryEnqueueControlBarrier(() => host.Calls.Add("mapping_change"), () => { }));
EnqueueCompute(host, queue, 0x20, Compute, 2, CreateInstanceCountPacket(2));
Assert.Equal(SliceResult.Completed, queue.ProcessOne());
Assert.DoesNotContain("mapping_change", host.Calls);
host.WriteDword(Label, 1);
queue.RetryBlocked();
Assert.Equal(SliceResult.Completed, queue.ProcessOne());
Assert.Equal(SliceResult.Completed, queue.ProcessOne());
var compute = host.Calls.IndexOf("begin 1 2");
var retriedGraphics = host.Calls.FindLastIndex(call => call == "begin 0 1");
var barrier = host.Calls.IndexOf("mapping_change");
Assert.True(compute >= 0 && retriedGraphics > compute && barrier > retriedGraphics, string.Join(", ", host.Calls));
}
[Fact]
public void ControlBarrier_IsCancelledWhenTheQueueIsDiscarded()
{
var (_, queue) = NewQueue();
var ran = false;
var cancelled = false;
Assert.True(queue.TryEnqueueControlBarrier(() => ran = true, () => cancelled = true));
queue.DiscardAll();
Assert.False(ran);
Assert.True(cancelled);
Assert.False(queue.HasPending);
}
[Fact]
public void ShutdownDrain_RunsAnAcceptedControlBarrier()
{
var (_, queue) = NewQueue();
var ran = false;
Assert.True(queue.TryEnqueueControlBarrier(() => ran = true, () => { }));
queue.StopAccepting();
Assert.Equal(IdleOutcome.Completed, queue.DrainForShutdown(cancelBlockedOnNoProgress: true));
Assert.True(ran);
Assert.False(queue.HasPending);
}
[Fact]
public void BlockedHead_IsRequeuedAndRetriedWithoutStoppingOtherQueues()
{

View File

@ -18,9 +18,9 @@ public sealed class GpuWorkerRelayTests : IDisposable
private readonly Thread _thread;
private bool _stop;
public TestWorker()
public TestWorker(Func<Action, Action, bool>? tryEnqueueAfterPendingWork = null)
{
Relay = new GpuWorkerRelay(Wake);
Relay = new GpuWorkerRelay(Wake, tryEnqueueAfterPendingWork);
_thread = new Thread(Run) { IsBackground = true };
}
@ -160,6 +160,116 @@ public sealed class GpuWorkerRelayTests : IDisposable
device.Complete(1);
}
[Fact]
public void AfterPendingWork_BypassesTheCommandStreamBarrier()
{
var barrierCalled = false;
using var worker = new TestWorker(
(_, _) =>
{
barrierCalled = true;
return true;
});
worker.Start();
var workRan = false;
var ranOnWorker = false;
Assert.True(worker.Relay.TryRunAfterPendingWork(() =>
{
workRan = true;
ranOnWorker = worker.Relay.IsGpuQueueThread;
}));
Assert.True(workRan);
Assert.True(ranOnWorker);
Assert.False(barrierCalled);
}
[Fact]
public void AfterPendingWork_ReturnsFalseWhenTheWorkerIsClosed()
{
var barrierCalled = false;
var relay = new GpuWorkerRelay(
() => { },
(_, _) =>
{
barrierCalled = true;
return true;
});
relay.StopAcceptingWork();
Assert.False(relay.TryRunAfterPendingWork(() => { }));
Assert.False(barrierCalled);
}
[Fact]
public async Task AfterAcceptedCommandStreams_WaitsForTheControlBarrier()
{
Action? run = null;
Action? cancel = null;
var relay = new GpuWorkerRelay(
() => { },
(work, cancellation) =>
{
run = work;
cancel = cancellation;
return true;
});
var workRan = false;
var completion = Task.Run(() =>
relay.TryRunAfterAcceptedCommandStreams(() => workRan = true));
Assert.True(WaitUntil(() => Volatile.Read(ref run) is not null));
Assert.False(await CompletesWithin(completion, 50));
run!();
Assert.True(await completion.WaitAsync(TimeSpan.FromSeconds(2)));
Assert.True(workRan);
Assert.NotNull(cancel);
}
[Fact]
public async Task AfterAcceptedCommandStreams_CancellationReleasesTheCaller()
{
Action? cancel = null;
var relay = new GpuWorkerRelay(
() => { },
(_, cancellation) =>
{
cancel = cancellation;
return true;
});
var completion = Task.Run(() => Record.Exception(() =>
relay.TryRunAfterAcceptedCommandStreams(() => { })));
Assert.True(WaitUntil(() => Volatile.Read(ref cancel) is not null));
cancel!();
Assert.IsType<OperationCanceledException>(
await completion.WaitAsync(TimeSpan.FromSeconds(2)));
}
[Fact]
public async Task AfterAcceptedCommandStreams_RethrowsTheWorkException()
{
Action? run = null;
var relay = new GpuWorkerRelay(
() => { },
(work, _) =>
{
run = work;
return true;
});
var expected = new InvalidOperationException("ordered state change failed");
var completion = Task.Run(() => Record.Exception(() =>
relay.TryRunAfterAcceptedCommandStreams(() => throw expected)));
Assert.True(WaitUntil(() => Volatile.Read(ref run) is not null));
run!();
Assert.Same(expected, await completion.WaitAsync(TimeSpan.FromSeconds(2)));
}
[Fact]
public void CommandsRunBeforeGuestWork()
{