[Tests] Cover ordered video-out state changes
Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com>
This commit is contained in:
parent
db05345cde
commit
2cecb5c552
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue